Introduction to Data Mining (IT326) Course Project Prepared by: Basma Alsulaim and Aafia Nawal Muhammad

1- Problem

    The problem we aim to address revolves around predicting the success of startups based on historical data encompassing their early-stage decisions. The dataset provides a comprehensive record of startup companies, tracing their early-stage decisions, such as funding rounds, categories, and more, spanning the years from 1984 to 2010. We strive to build a predictive model, particularly using Decision Trees, to anticipate the outcome of startups.

    The ability to predict startup success is important for both investors and job seekers. For investors, it serves as a strategic tool to identify startups with a higher likelihood of success, leading to more informed investment decisions and better returns. On the flip side, job seekers can benefit by identifying promising companies, increasing the likelihood of a fruitful and stable career path.


2- Data Mining Task

    The data mining task involves two key aspects: classification and clustering. In the classification part, the goal is to create a predictive model that sorts startups into ‘acquired’ or ‘closed’ categories using the class label ‘status’. On the clustering part, the aim is to uncover patterns and structures within the dataset, identifying groups of startups with similar traits. This dual approach aims to provide a comprehensive understanding of startup outcomes, offering predictive insights and revealing underlying structures in the startup dataset.

Goals:

  1. Prediction: Create a predictive model to predict if a startup will succeed or close, using past data for better decision-making.

  2. Pattern Discovery: Find hidden patterns in startup data, helping us understand common traits among them.

  3. Smart Investing: Assist investors in making strategic decisions by identifying startups with a high chance of success, leading to better returns.


3- Data Description

      ◆ Source: Access the dataset on Kaggle through the following link: https://www.kaggle.com/datasets/manishkc06/startup-success-prediction

      ◆ Number of objects: The dataset holds 925 rows of data before preprocessing.

      ◆ Number of attributes: The dataset holds 50 columns of data before preprocessing.

      ◆ Class label: The class label for this data set is status, which holdes two states: “acquired” (i.e. successful company) or “closed” (i.e. unsuccessful company).

      ◆ Types of attributes: Nominal, Numerical, Binary

Data Disctionary:

Attribute Name Description Type Possible Values
Unnamed: 0 Method of numbering companies. Nominal 1 to 1153
state_code The code of the state the startup was founded in. Nominal Different state codes. Like CA, AZ
latitude The latitude of the startup headquarters. Numerical 90 to -90
longitude The longitude of the startup headquarters. Numerical 0 to 180
zip_code The zip code of the city the startup was founded in. Nominal Different random city zipcodes
id Irrelevant* Nominal Data in unrecognizable format c:number
city The name of the city the startup was founded in. Nominal Different cities. Like Palo Alto, Mountain View
Unnamed: 6 The address of the headquarters. Nominal Different addresses. Like San Diego CA 92121
name The name of the startup. Nominal Different names. Like Qsecure, drop.io
labels Irrelevant* Binary 0 or 1
founded_at Startup founding date. Nominal 01 1984 to 09 2010
closed_at Startup closing date. Nominal 01 2001 to 08 2013
first_funding_at Startup first funding date. Nominal 01 2000 to 09 2009
last_funding_at Startup last funding date. Nominal 01 2001 to 09 2011
age_first_funding_year The average age of startup when received first funding. Numerical 0 to 21
age_last_funding_year The average age of startup when received last funding. Numerical 0 to 21
age_first_milestone_year The average age of startup when acheived first milestone. Numerical 0 to 24
age_last_milestone_year The average age of startup when acheived last milestone. Numerical 0 to 24
relationships Irrelevant* Numerical 0 to 63
funding_rounds The number of funding rounds the startup went through. Numerical 1 to 10
funding_total_usd The total number of money in USD raised by the startup. Numerical 11000 to 5700000000
milestones The total number of milestones achieved by the startup. Numerical 0 to 8
state_code.1 The code of the state the startup was founded in. Nominal Different state codes. Like CA, AZ
is_CA If the startup was founded in California. Binary 0 or 1
is_NY If the startup was founded in New York. Binary 0 or 1
is_MA If the startup was founded in Massachusetts. Binary 0 or 1
is_TX If the startup was founded in Texas. Binary 0 or 1
is_otherstate If the startup was founded in a state other than the listed. Binary 0 or 1
category_code The sector of the startup. Nominal Different categories. Like biotech, mobile
is_software If the startup sector is software. Binary 0 or 1
is_web If the startup sector is web. Binary 0 or 1
is_mobile If the startup sector is mobile. Binary 0 or 1
is_enterprise If the startup sector is enterprise. Binary 0 or 1
is_advertising If the startup sector is advertising. Binary 0 or 1
is_gamesvideo If the startup sector is video games. Binary 0 or 1
is_ecommerce If the startup sector is ecommerce. Binary 0 or 1
is_biotech If the startup sector is biotech. Binary 0 or 1
is_consulting If the startup sector is consulting. Binary 0 or 1
is_othercategory If the startup sector is any other category that the listed. Binary 0 or 1
object_id Irrelevant* Nominal Data in unrecognizable format c:number
has_VC If the startup has a venture capitalist** investor. Binary 0 or 1
has_angel If the startup has an angel*** investor. Binary 0 or 1
has_roundA If the startup went through a series A funding round. Binary 0 or 1
has_roundB If the startup went through a series B funding round. Binary 0 or 1
has_roundC If the startup went through a series C funding round. Binary 0 or 1
has_roundD If the startup went through a series D funding round. Binary 0 or 1
avg_participants The average number of participants in the startup. Binary 0 or 1
is_top500 If the startup is a top 500 company. Binary 0 or 1
status The acquisition status of the startup. Binary “acquired” or “closed”

*The original dataset source lacks any description for these attributes. We can only speculate their meanings. They could refer to the number of data objects, a method for organizing data, or a recording system of some sort. Furthermore, since they pose no importance to the startup prediction model, we will designate them as “irrelevant” and proceed to remove them from the dataset in the subsequent steps.

**VC stands for Venture Capitalist. Which is a type of investor that invests the money of a venture capital firm into small startup companies.

***Angel stands for Angel Investor. Which is a type of investor that invests their personal money into small startup companies in exchange for a percentage in the company.


### Installing necessary packages: Preprocessing and Visualization

install.packages("readxl")
Error in install.packages : Updating loaded packages
install.packages("lubridate")
trying URL 'https://cran.rstudio.com/bin/macosx/big-sur-arm64/contrib/4.3/lubridate_1.9.3.tgz'
Content type 'application/x-gzip' length 1000867 bytes (977 KB)
========

Restarting R session...


##### Load necessary packages: Classification

install.packages("partykit")
install.packages("rpart")
install.packages("rpart.plot")
install.packages("ROSE")
install.packages("caret")
install.packages("C50")
library(party)
library(partykit)
library(rpart.plot)
library(RWeka)
library(caret)
library(rpart)
library(ggplot2)
library(lattice)

Load necessary packages: Classification

install.packages("dplyr")
install.packages("ClusterR")
install.packages("cluster") # To make clusters
install.packages("factoextra") # To visualize and validate the clusters

Import Dataset

library(readxl)
dataset <- read_excel("Original_StartupData.xlsx")
View(dataset)

To import and load the excel dataset for data preprocessing and mining.

Assessing Class Label Balance Before Pre-Processing

library(ggplot2)
library(grid)

# Create a bar plot for the "status" attribute
gg <- ggplot(dataset, aes(x = status)) +
  geom_bar(fill = "darkgray", color = "black") +
  labs(title = "Distribution of Startup Status", x = "Status", y = "Count") +
  theme(plot.title = element_text(hjust = 0.5))

# Print the plot
print(gg)

# Add an external annotation to the right side
grid.text("1 = Acquired\n0 = Closed", x = 0.9, y = 0.92, just = c("right", "top"), gp = gpar(fontsize = 12, col = "black"))

As shown above, there is a class imbalance in the class label (status). In the dataset, objects with acquired status are almost twice as much as objects with closed objects. This indicates that the class “acquired” is more prevalent than the class “closed” in the dataset. In later steps, this may present a challenge in training and testing the model.

Sample of Raw Dataset and Summary:

Here, we aim to explore the data of our dataset. Five-number summary, variance, missing values, and graphs are utilized to give insight into the data we are dealing with. Nominal, numerical, and binary data will be examined.

str(dataset)
tibble [923 × 49] (S3: tbl_df/tbl/data.frame)
 $ Unnamed: 0              : num [1:923] 1005 204 1001 738 1002 ...
 $ state_code              : chr [1:923] "CA" "CA" "CA" "CA" ...
 $ latitude                : num [1:923] 42.4 37.2 32.9 37.3 37.8 ...
 $ longitude               : num [1:923] -71.1 -122 -117.2 -122.1 -122.4 ...
 $ zip_code                : chr [1:923] "92101" "95032" "92121" "95014" ...
 $ id                      : chr [1:923] "c:6669" "c:16283" "c:65620" "c:42668" ...
 $ city                    : chr [1:923] "San Diego" "Los Gatos" "San Diego" "Cupertino" ...
 $ Unnamed: 6              : chr [1:923] NA NA "San Diego CA 92121" "Cupertino CA 95014" ...
 $ name                    : chr [1:923] "Bandsintown" "TriCipher" "Plixi" "Solidcore Systems" ...
 $ labels                  : num [1:923] 1 1 1 1 0 0 1 1 1 1 ...
 $ founded_at              : chr [1:923] "1/1/2007" "1/1/2000" "3/18/2009" "1/1/2002" ...
 $ closed_at               : chr [1:923] NA NA NA NA ...
 $ first_funding_at        : chr [1:923] "4/1/2009" "2/14/2005" "3/30/2010" "2/17/2005" ...
 $ last_funding_at         : chr [1:923] "1/1/2010" "12/28/2009" "3/30/2010" "4/25/2007" ...
 $ age_first_funding_year  : num [1:923] 2.25 5.13 1.03 3.13 0 ...
 $ age_last_funding_year   : num [1:923] 3 10 1.03 5.32 1.67 ...
 $ age_first_milestone_year: num [1:923] 4.6685 7.0055 1.4575 6.0027 0.0384 ...
 $ age_last_milestone_year : num [1:923] 6.7041 7.0055 2.2055 6.0027 0.0384 ...
 $ relationships           : num [1:923] 3 9 5 5 2 3 6 25 13 14 ...
 $ funding_rounds          : num [1:923] 3 4 1 3 2 1 3 3 3 3 ...
 $ funding_total_usd       : num [1:923] 375000 40100000 2600000 40000000 1300000 7500000 26000000 34100000 9650000 5750000 ...
 $ milestones              : num [1:923] 3 1 2 1 1 1 2 3 4 4 ...
 $ state_code.1            : chr [1:923] "CA" "CA" "CA" "CA" ...
 $ is_CA                   : num [1:923] 1 1 1 1 1 1 1 1 0 1 ...
 $ is_NY                   : num [1:923] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_MA                   : num [1:923] 0 0 0 0 0 0 0 0 1 0 ...
 $ is_TX                   : num [1:923] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_otherstate           : num [1:923] 0 0 0 0 0 0 0 0 0 0 ...
 $ category_code           : chr [1:923] "music" "enterprise" "web" "software" ...
 $ is_software             : num [1:923] 0 0 0 1 0 0 1 0 0 0 ...
 $ is_web                  : num [1:923] 0 0 1 0 0 0 0 0 0 1 ...
 $ is_mobile               : num [1:923] 0 0 0 0 0 0 0 0 1 0 ...
 $ is_enterprise           : num [1:923] 0 1 0 0 0 0 0 0 0 0 ...
 $ is_advertising          : num [1:923] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_gamesvideo           : num [1:923] 0 0 0 0 1 0 0 0 0 0 ...
 $ is_ecommerce            : num [1:923] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_biotech              : num [1:923] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_consulting           : num [1:923] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_othercategory        : num [1:923] 1 0 0 0 0 1 0 1 0 0 ...
 $ object_id               : chr [1:923] "c:6669" "c:16283" "c:65620" "c:42668" ...
 $ has_VC                  : num [1:923] 0 1 0 0 1 0 1 0 1 1 ...
 $ has_angel               : num [1:923] 1 0 0 0 1 0 0 0 0 1 ...
 $ has_roundA              : num [1:923] 0 0 1 0 0 0 1 1 1 1 ...
 $ has_roundB              : num [1:923] 0 1 0 1 0 1 1 1 0 0 ...
 $ has_roundC              : num [1:923] 0 1 0 1 0 0 0 0 0 0 ...
 $ has_roundD              : num [1:923] 0 1 0 1 0 0 0 1 1 0 ...
 $ avg_participants        : num [1:923] 1 4.75 4 3.33 1 ...
 $ is_top500               : num [1:923] 0 1 1 1 1 1 1 1 1 1 ...
 $ status                  : chr [1:923] "acquired" "acquired" "acquired" "acquired" ...

Above is a snapshot of the raw dataset. The startup data is a collection of different data types.

A) Numerical attributes:

i) Five-number summary:

Below, a five-number summary is performed on numerical attributes to gain insights into the distribution and characteristics of the numeric data. The five number summary is represented by the minimum, first quartile (Q1), median (Q2), third quartile (Q3), and maximum of the available numeric attributes.

# Specify numerical attributes for the five-number summary
numerical_attributes <- c("latitude", "longitude", 
                          "age_first_funding_year", "age_last_funding_year", 
                          "age_first_milestone_year", "age_last_milestone_year", 
                          "relationships", "funding_rounds", "funding_total_usd", 
                          "milestones", "avg_participants")

# Subset the data to include only numerical attributes
numerical_data <- dataset[, numerical_attributes]

# Calculate the five-number summary
summary_data <- summary(numerical_data)
print(summary_data)
    latitude       longitude       age_first_funding_year age_last_funding_year age_first_milestone_year age_last_milestone_year relationships    funding_rounds   funding_total_usd  
 Min.   :25.75   Min.   :-122.76   Min.   :-9.0466        Min.   :-9.047        Min.   :-14.170          Min.   :-7.005          Min.   : 0.000   Min.   : 1.000   Min.   :1.100e+04  
 1st Qu.:37.39   1st Qu.:-122.20   1st Qu.: 0.5767        1st Qu.: 1.670        1st Qu.:  1.000          1st Qu.: 2.411          1st Qu.: 3.000   1st Qu.: 1.000   1st Qu.:2.725e+06  
 Median :37.78   Median :-118.37   Median : 1.4466        Median : 3.529        Median :  2.521          Median : 4.477          Median : 5.000   Median : 2.000   Median :1.000e+07  
 Mean   :38.52   Mean   :-103.54   Mean   : 2.2356        Mean   : 3.931        Mean   :  3.055          Mean   : 4.754          Mean   : 7.711   Mean   : 2.311   Mean   :2.542e+07  
 3rd Qu.:40.73   3rd Qu.: -77.21   3rd Qu.: 3.5753        3rd Qu.: 5.560        3rd Qu.:  4.686          3rd Qu.: 6.753          3rd Qu.:10.000   3rd Qu.: 3.000   3rd Qu.:2.472e+07  
 Max.   :59.34   Max.   :  18.06   Max.   :21.8959        Max.   :21.896        Max.   : 24.685          Max.   :24.685          Max.   :63.000   Max.   :10.000   Max.   :5.700e+09  
                                                                                NA's   :152              NA's   :152                                                                  
   milestones    avg_participants
 Min.   :0.000   Min.   : 1.000  
 1st Qu.:1.000   1st Qu.: 1.500  
 Median :2.000   Median : 2.500  
 Mean   :1.842   Mean   : 2.839  
 3rd Qu.:3.000   3rd Qu.: 3.800  
 Max.   :8.000   Max.   :16.000  
                                 

As demonstrated above by the five-number summary, some attributes contain negative values. Negative values in attributes related to age and longitude are illogical and likely errors. We will address this issue by removing these negative values during the data cleaning process.

ii) Variance:
# List of numerical attributes
numerical_attributes <- c("latitude", "longitude",
                          "age_first_funding_year", "age_last_funding_year",
                          "age_first_milestone_year", "age_last_milestone_year",
                          "relationships", "funding_rounds", "funding_total_usd",
                          "milestones", "avg_participants")

# Calculate variances for numerical attributes
variances_numerical <- sapply(dataset[, numerical_attributes], var)

# Print variances for numerical attributes
for (i in seq_along(numerical_attributes)) {
  cat("Variance for", paste(numerical_attributes[i], ":", variances_numerical[i]), "\n")
}
Variance for latitude : 13.9988005686463 
Variance for longitude : 501.498703976022 
Variance for age_first_funding_year : 6.30235186954307 
Variance for age_last_funding_year : 8.80848885758838 
Variance for age_first_milestone_year : NA 
Variance for age_last_milestone_year : NA 
Variance for relationships : 52.791500882485 
Variance for funding_rounds : 1.93466321036515 
Variance for funding_total_usd : 35961192195068924 
Variance for milestones : 1.74935546870411 
Variance for avg_participants : 3.51412871963395 

The variance can be classified into four classes: low, moderate, high, and very high. Attributes age_first_milestone_year and age_last_milestone_year include missing values which prevents an accurate assessment of variability.

Attributes longitude, and funding_total_usd attribute demonstrates an exceptionally high level of variability, likely influenced by outliers or a broad range of funding values.

The relationships attribute shows a high level of variability, indicating a wide range of values among startups.

Attributes latitude and age_last_funding_year display a moderate level of variance with respect to the dataset.

Attributes of low variance are age_first_funding_year, funding_rounds, and milestones, which might be due to their minimal range resulting in a more concentrated distribution of values. Which brings us to the third numerical data assessment metric:

iii) Mising values:
# List of numerical attributes
numerical_attributes <- c("latitude", "longitude", "zip_code", 
                          "age_first_funding_year", "age_last_funding_year", 
                          "age_first_milestone_year", "age_last_milestone_year", 
                          "relationships", "funding_rounds", "funding_total_usd", 
                          "milestones", "avg_participants")

missing_numerical_values <- sapply(dataset[, numerical_attributes], function(x) sum(is.na(x)))
total_numerical_values <- sum(missing_numerical_values)

print(total_numerical_values)
[1] 304

There is a total of 304 missing values in the columns of numerical attributes. We’ll address this issue by removing these missing values during the data cleaning process.

B) Binary and Nominal Attributes:

i) Five-number summary:

Since we are addressing binary and nominal data, the five-number summary won’t give us useful insights. Therefore, we won’t do the five-number summary for those types of attributes.

ii) Variance:

Variance cannot be performed on nominal attributes, so we will only conduct it on binary attributes.

# List of binary attributes
binary_attributes <- c("is_CA", "is_NY", "is_MA", "is_TX", "is_otherstate",
                        "is_software", "is_web", "is_mobile", "is_enterprise",
                        "is_advertising", "is_gamesvideo", "is_ecommerce",
                        "is_biotech", "is_consulting", "is_othercategory",
                        "has_VC", "has_angel", "has_roundA", "has_roundB",
                        "has_roundC", "has_roundD", "labels", "is_top500", "status")

# Calculate variances for binary attributes
variances_binary <- sapply(dataset[, binary_attributes], var)
Warning: NAs introduced by coercion
# Print variances for binary attributes
for (i in seq_along(binary_attributes)) {
  cat("Variance for", paste(binary_attributes[i], ":", variances_binary[i]), "\n")
}
Variance for is_CA : 0.24950705400432 
Variance for is_NY : 0.101764264881798 
Variance for is_MA : 0.0819265669102227 
Variance for is_TX : 0.0434803044866903 
Variance for is_otherstate : 0.172356011590988 
Variance for is_software : 0.138436156736849 
Variance for is_web : 0.131815756880681 
Variance for is_mobile : 0.0783496238569404 
Variance for is_enterprise : 0.0729137044862202 
Variance for is_advertising : 0.0627281123752363 
Variance for is_gamesvideo : 0.0532217164156303 
Variance for is_ecommerce : 0.0263805425578666 
Variance for is_biotech : 0.0355179634456162 
Variance for is_consulting : 0.0032432203768246 
Variance for is_othercategory : 0.218858621443326 
Variance for has_VC : 0.2200007990543 
Variance for has_angel : 0.189986909610505 
Variance for has_roundA : 0.250205051433248 
Variance for has_roundB : 0.238637565422572 
Variance for has_roundC : 0.178870654260958 
Variance for has_roundD : 0.0898372044380429 
Variance for labels : 0.228696389919692 
Variance for is_top500 : 0.154490097602133 
Variance for status : NA 

These values indicate how much the values in each binary attribute deviate from their mean (0.5 for balanced binary variables). Lower variances suggest that the values are closer to the mean, while higher variances indicate greater spread. Since the variance for all these binary attributes is very low, the variance indicates no further significance.

Although status is a binary attribute, it hasn’t been encoded to 1 (acquired) and 0 (closed) yet. So, its variance is denoted by NA. As it seems a nominal attribute.

iii) Mising values:
# List of attributes
binary_nominal_attributes <- c("Unnamed: 0", "state_code", "id", "zip_code", "city", "Unnamed: 6",
                          "name", "founded_at", "closed_at", "first_funding_at",
                          "last_funding_at", "state_code.1", "category_code",
                          "object_id", "is_CA", "is_NY", "is_MA", "is_TX", "is_otherstate",
                          "is_software", "is_web", "is_mobile", "is_enterprise",
                          "is_advertising", "is_gamesvideo", "is_ecommerce",
                          "is_biotech", "is_consulting", "is_othercategory",
                          "has_VC", "has_angel", "has_roundA", "has_roundB",
                          "has_roundC", "has_roundD", "labels", "is_top500", "status")

missing_binary_nominal_values <- sapply(dataset[, binary_nominal_attributes], function(x) sum(is.na(x)))
total_binary_nominal_values <- sum(missing_binary_nominal_values)

print(total_binary_nominal_values)
[1] 1082

There is a total of 1082 missing values in the columns of binary and nominal attributes. We’ll address this issue by removing these missing values during the data cleaning process.

Detecting and Dealing With Missing Values

We will detect and eliminate missing values in the dataset to ensure the creation of representative graphs, as plotting requires addressing missing values first.

sum(is.na(dataset))
[1] 1386

There is a total of 1386 missing values in the startup dataset.

# Calculate the sum of missing values for each attribute
sum_missing_values <- function(attribute) {
  sum(is.na(attribute))
}

# Apply the function to each attribute and store the results in a data frame
missing_values <- data.frame(
  Attribute = names(dataset),
  Missing_Values = sapply(dataset, sum_missing_values)
)

# Print the result
print(missing_values)

The table above shows each attribute with the number of missing values found in its column. All missing values come from five attributes, ordered from most missing to least: closed_at, Unnamed: 6, age_first_milestone_year, age_last_milestone_year, and state_code.1.

Replacing Missing Values

For attribute closed_at and state_code.1, we will fill all missing values with the global constant N/A because information about the operation of the company (whether is is still open or closed) remains unknown and cannot be replaced with the average as the company may still be operating.

Note: state_code.1 is a duplicate attribute. Unnamed: 6 is an irrelevant attribute as explained at the beginning of this section. Both state_code.1 and Unnamed: 6 will be removed in the data reduction step.

Before replacing missing values of attribute closed_at with N/A:

head(dataset$closed_at)
[1] NA          NA          NA          NA          "10/1/2012" "2/15/2009"

As shown in the table above, missing values are automatically denoted as “NA” in R.

Replacing with “N/A”:

dataset$closed_at[is.na(dataset$closed_at)] <- "N/A"
dataset$state_code.1[is.na(dataset$state_code.1)] <- "N/A"
dataset$'Unnamed: 6'[is.na(dataset$'Unnamed: 6')] <- "N/A"

This code chunk replaces all missing values in attribute closed_at with N/A.

After replacing missing values of attribute closed_at with N/A:

head(dataset$closed_at)
[1] "N/A"       "N/A"       "N/A"       "N/A"       "10/1/2012" "2/15/2009"

As shown in the table above, missing values are replaced with global constant “N/A”.

For attributes age_first_milestone_year and age_last_milestone_year we will replace missing values with the attribute’s mean.

Before replacing missing values of attributes age_first_milestone_year and age_last_milestone_year with average:

row_13 <- dataset[13, c("age_first_milestone_year", "age_last_milestone_year")]

# Display the result
print(row_13)

As shown in row 13 of the dataset, missing values are automatically denoted as “NA” in R.

Replacing with average:

dataset$age_first_milestone_year = ifelse (is.na(dataset$age_first_milestone_year), ave(dataset$age_first_milestone_year, FUN=function(x)mean(x,na.rm=TRUE)), dataset$age_first_milestone_year)

dataset$age_last_milestone_year = ifelse (is.na(dataset$age_last_milestone_year), ave(dataset$age_last_milestone_year, FUN=function(x)mean(x,na.rm=TRUE)), dataset$age_last_milestone_year)

This code chunk replaces all missing values of attributes age_first_milestone_year and age_last_milestone_year with their average.

After replacing missing values of attributes age_first_milestone_year and age_last_milestone_year with average:

row_13 <- dataset[13, c("age_first_milestone_year", "age_last_milestone_year")]

# Display the result
print(row_13)

As shown in row 13 of the dataset, missing values are replaced with the attribute average.

Check for remaining missing values:

sum(is.na(dataset))
[1] 0

All missing values are addressed. There are no remaining missing values.

Graphical Representation of the Dataset ( Before ) Data Pre-Processing


A) Numerical Attributes Histogram:

# Load the required libraries
library(ggplot2)

# Select numerical attributes for histograms
numerical_attributes <- c("latitude", "longitude",
                           "age_first_funding_year", "age_last_funding_year",
                           "age_first_milestone_year", "age_last_milestone_year",
                           "relationships", "funding_rounds", "funding_total_usd",
                           "milestones", "avg_participants")

# Melt the dataset for easier plotting
melted_data <- reshape2::melt(dataset[, numerical_attributes])
No id variables; using all as measure variables
# Create histogram with facet wrap
histogram_plot_numerical_before <- ggplot(melted_data, aes(x = value)) +
  geom_histogram(binwidth = 1, fill = "darkgray", color = "black") +
  facet_wrap(~variable, scales = "free") +
  labs(title = "Histogram for Numerical Attributes BEFORE Pre-processing", x = "Value", y = "Frequency") +
  theme(plot.title = element_text(hjust = 0.5))

# Display the plot
par(mfrow = c(1, 2))  # Set up a 1x2 plotting grid
plot(histogram_plot_numerical_before)  # Plot the original graph

In the collection of histograms above, we can see that there is a noticeable distribution among values of certain attributes. As shown, “funding_total_usd” is not appearing as expected, it could be due to the presence of extreme values or outliers that are affecting the visualization. The graphs above indicate that we must remove outliers and deal with negative values.

# Assuming your dataset is named 'your_dataset'
# Replace 'your_dataset' with the actual name of your dataset

# Select numerical attributes for histograms
numerical_attributes <- c("latitude", "longitude",
                           "age_first_funding_year", "age_last_funding_year",
                           "age_first_milestone_year", "age_last_milestone_year",
                           "relationships", "funding_rounds", "funding_total_usd",
                           "milestones", "avg_participants")

# Melt the dataset for easier plotting
melted_data <- reshape2::melt(dataset[, numerical_attributes])
No id variables; using all as measure variables
# Create histogram with facet wrap, log-transforming funding_total_usd
histogram_plot <- ggplot(melted_data, aes(x = log(value + 1))) +
  geom_histogram(binwidth = 0.2, fill = "darkgray", color = "black") +
  facet_wrap(~variable, scales = "free") +
  labs(title = "Histogram for Numerical Attributes BEFORE Pre-processing",
       x = "Log(Value + 1)",
       y = "Frequency")+
  theme(plot.title = element_text(hjust = 0.5))

# Display the plot
print(histogram_plot)

Here, we fixed the issue regarding the “funding_total_usd” graph. We used the log(value + 1) to log-transform the “funding_total_usd” values. Although the “funding_total_usd” graph appeared, we have eliminated negative values. This indicates that the graphical representation above is not an accurate representation of the raw numerical attributes but an estimate of one.

B) Nominal Attributes Barplot:

Attributes Unnamed: 0, id, zip_code, Unnamed: 6, object_id are used for identification purposes and do not have mathematical significance. And since they are unique unrepetitive strings of numbers, a barplot is not necessary to represent them. In addition, they will be removed later in the data reduction step.

Moreover, attributes founded_at, closed_at, first_funding_at, and last_funding_at represent dates in the format mm/yy/dd. We can say that they are semi-unique since the probability of two rows sharing the same date is extremely low and holds no significance. Therefore, no barplot is necessary to represent them.

library(tidyr)

# Selecting specific columns for visualization
columns_to_visualize <- dataset[, c("state_code", "city", "category_code", "state_code.1")]

# Melt the required columns for visualization
melted_data_before <- gather(data = columns_to_visualize)

# Plotting bar graphs for the specified attributes and facet_wrap
barplot_nominal_before <- ggplot(melted_data_before, aes(x = value, fill = key)) +
  geom_bar(position = "dodge", stat = "count", color = "black", fill = "darkgray") +
  facet_wrap(~key, scales = "free") +
  labs(title = "Nominal Attributes BEFORE Pre-processing", x = "Values", y = "Count") +
  theme(plot.title = element_text(hjust = 0.5))

# Displaying the plot
print(barplot_nominal_before)

From the graphs, we can see that state_code and state_code.1 are redundant which requires the elimination of one of them later in the data reduction step. In addition, we can see which state code, city, and category code most startups shared.

Attributes Unnamed: 0, id, zip_code, Unnamed: 6, and object_id cannot be plotted as they are unique attributes. They pertain no significance to the dataset and will be removed later on in data reduction.

C) Binary Attributes Barplot:

library(tidyr)

# List of binary attributes
binary_attributes <- c("is_CA", "is_NY", "is_MA", "is_TX", "is_otherstate", 
                       "is_software", "is_web", "is_mobile", "is_enterprise", 
                       "is_advertising", "is_gamesvideo", "is_ecommerce", 
                       "is_biotech", "is_consulting", "is_othercategory", 
                       "has_VC", "has_angel", "has_roundA", "has_roundB", 
                       "has_roundC", "has_roundD", "labels", "is_top500", "status")

# Melt the datasets for easier plotting
melted_data <- gather(dataset, key = "variable", value = "value", all_of(binary_attributes))

# Create bar plots with facet wrap
barplot_binary_before <- ggplot(melted_data, aes(x = value, fill = variable)) +
  geom_bar(position = "dodge", stat = "count", color = "black", fill = "darkgray") +
  facet_wrap(~variable, scales = "free") +
  labs(title = "Bar Plots for Binary Attributes BEFORE Pre-processing", x = "Value", y = "Count") +
  theme(plot.title = element_text(hjust = 0.5))

# Display the plot
print(barplot_binary_before)

The barplots provide insights into the startup landscape from 1984 to 2010, revealing trends such as the states with the highest startup activity, the most prevalent category types, preferred investment rounds, the likelihood of a startup becoming a top 500 company, and their overall status.


4-Data Pre-processing

Data pre-procesing is essential in producing accurate results. Because correct pre-processing leads to correct results, and vice versa. We will prepare the startup data for data mining by following the data pre-processing steps: 1-Data Cleaning 2-Data Integration 3-Data Reduction 4-Data Transformation

4.1-Data Cleaning

In data cleaning we are ensuring accuracy and reliability by identifying replacing negative values and removing outliers from the dataset.

A) Negative Values

Negative values in attributes related to age and longitude do not make sense. So, we are going to remove the negative sign from the values.

Before removing negative sign of attributes age_first_funding_year, age_last_funding_year, age_first_milestone_year, age_last_milestone_year, and longitude:
row_235 <- dataset[235, c("longitude", "age_first_funding_year", "age_last_funding_year", "age_first_milestone_year", "age_last_milestone_year")]

# Display the result
print(row_235)

As depicted, attributes above contain negative values.

Removing negative values:
# Remove the negative sign from age_first_funding_year
dataset$age_first_funding_year <- abs(dataset$age_first_funding_year)

# Remove the negative sign from age_last_funding_year
dataset$age_last_funding_year <- abs(dataset$age_last_funding_year)

# Remove the negative sign from age_first_milestone_year
dataset$age_first_milestone_year <- abs(dataset$age_first_milestone_year)

# Remove the negative sign from age_last_milestone_year
dataset$age_last_milestone_year <- abs(dataset$age_last_milestone_year)

# Remove the negative sign from longitude
dataset$longitude <- abs(dataset$longitude)

This code chunk removes the negative sign from the attributes.

After removing negative sign of attributes age_first_funding_year, age_last_funding_year, age_first_milestone_year, age_last_milestone_year, and longitude:
row_235 <- dataset[235, c("longitude", "age_first_funding_year", "age_last_funding_year", "age_first_milestone_year", "age_last_milestone_year")]

# Display the result
print(row_235)

As shown, none of the attributes above have a negative sign as they have all become positive numbers.

B) Outliers

We will identify and eliminate outliers present in the numerical attributes of the dataset. Outliers, or data points that significantly deviate from the majority, can skew statistical analyses and affect the accuracy of models. By detecting and removing these outliers, we aim to ensure a more robust and representative dataset for subsequent analyses.

Binary and nominal attributes, are discrete and categorical. They don’t exhibit outliers as numerical values do. Outliers are specific to numerical data where values significantly deviate from the rest of the dataset. In the case of binary attributes, these represent two categories (0 or 1), so there aren’t outliers as there’s no numerical range or sequence to measure extremes. For nominal attributes, which represent categories without inherent order (like names) outliers don’t exist in the same sense as they do in numerical data. Outliers in numerical data might suggest errors, anomalies, or extremes in the data.

Numerical Attribute Boxplots Before Removing Outliers:
library(ggplot2)

# Select numerical attributes for boxplots
numerical_attributes <- c("latitude", "longitude",
                           "age_first_funding_year", "age_last_funding_year",
                           "age_first_milestone_year", "age_last_milestone_year",
                           "relationships", "funding_rounds", "funding_total_usd",
                           "milestones", "avg_participants")

# Melt the dataset for easier plotting
melted_data <- reshape2::melt(dataset[, numerical_attributes])
No id variables; using all as measure variables
# Create boxplots with facet wrap
boxplot_plot <- ggplot(melted_data, aes(x = variable, y = value)) +
  geom_boxplot(fill = "darkgray", color = "black") +
  facet_wrap(~variable, scales = "free") +
  labs(title = "Boxplots for Numerical Attributes Before Removing Outliers",
       x = "Attribute",
       y = "Value") +
  theme(plot.title = element_text(hjust = 0.5))

# Display the plot
print(boxplot_plot)

The box represents the interquartile range (IQR), which is the range between the first quartile (Q1) and the third quartile (Q3). The length of the box indicates the spread of the middle 50% of the data. The whiskers extend from the box to the minimum and maximum values within a certain range. By default, this range is 1.5 times the IQR. Points beyond the whiskers are outliers. As demonstrated above, every boxplot contains outliers that fall beyond the whiskers represented by individual points.

Overview of outliers:
# Attributes to check for outliers
attributes_to_check <- c("latitude", "longitude", "age_first_funding_year", 
                         "age_last_funding_year", "age_first_milestone_year", 
                         "age_last_milestone_year", "relationships", "funding_rounds", 
                         "funding_total_usd", "milestones", "avg_participants")

# Calculate the number of outliers for each attribute
outlier_counts <- sapply(attributes_to_check, function(attr) {
  Q1 <- quantile(dataset[[attr]], 0.25, na.rm = TRUE)
  Q3 <- quantile(dataset[[attr]], 0.75, na.rm = TRUE)
  IQR <- Q3 - Q1
  lower_bound <- Q1 - 1.5 * IQR
  upper_bound <- Q3 + 1.5 * IQR
  sum(dataset[[attr]] < lower_bound | dataset[[attr]] > upper_bound, na.rm = TRUE)
})

# Create a table with attribute names and their respective outlier counts
outlier_table <- data.frame(Attribute = attributes_to_check, Outlier_Count = outlier_counts)
print(outlier_table)

The table above represents the number of outliers for each attribute.

Removing Outliers:
## age_first_funding_year

# Calculate the quartiles
Q1 <- quantile(dataset$age_first_funding_year, 0.25)
Q3 <- quantile(dataset$age_first_funding_year, 0.75)

# Calculate the IQR
IQR <- Q3 - Q1

# Define the lower and upper bounds for outliers
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR

# Remove outliers from the dataset using dplyr
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
dataset_clean1 <- dataset %>%
  filter(age_first_funding_year >= lower_bound, age_first_funding_year <= upper_bound)

# Print the dimensions of the cleaned dataset
cat("funding_total_usd","\n")
funding_total_usd 
cat("Original dataset dimensions: ", dim(dataset), "\n")
Original dataset dimensions:  923 49 
cat("Cleaned dataset dimensions: ", dim(dataset_clean1), "\n","\n")
Cleaned dataset dimensions:  902 49 
 
dataset <- dataset_clean1

#################################
## age_last_funding_year

Q1 <- quantile(dataset$age_last_funding_year, 0.25)
Q3 <- quantile(dataset$age_last_funding_year, 0.75)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
library(dplyr)
dataset_clean2 <- dataset %>%
  filter(age_last_funding_year >= lower_bound, age_last_funding_year <= upper_bound)

cat("age_last_funding_year","\n")
age_last_funding_year 
cat("Original dataset dimensions: ", dim(dataset), "\n")
Original dataset dimensions:  902 49 
cat("Cleaned dataset dimensions: ", dim(dataset_clean2), "\n","\n")
Cleaned dataset dimensions:  895 49 
 
dataset <- dataset_clean2

#################################
## age_first_milestone_year

Q1 <- quantile(dataset$age_first_milestone_year, 0.25)
Q3 <- quantile(dataset$age_first_milestone_year, 0.75)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
library(dplyr)
dataset_clean3 <- dataset %>%
  filter(age_first_milestone_year >= lower_bound, age_first_milestone_year <= upper_bound)

cat("age_first_milestone_year","\n")
age_first_milestone_year 
cat("Original dataset dimensions: ", dim(dataset), "\n")
Original dataset dimensions:  895 49 
cat("Cleaned dataset dimensions: ", dim(dataset_clean3), "\n","\n")
Cleaned dataset dimensions:  864 49 
 
dataset <- dataset_clean3

#################################
## age_last_milestone_year

Q1 <- quantile(dataset$age_last_milestone_year, 0.25)
Q3 <- quantile(dataset$age_last_milestone_year, 0.75)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
library(dplyr)
dataset_clean4 <- dataset %>%
  filter(age_last_milestone_year >= lower_bound, age_last_milestone_year <= upper_bound)

cat("age_last_milestone_year","\n")
age_last_milestone_year 
cat("Original dataset dimensions: ", dim(dataset), "\n")
Original dataset dimensions:  864 49 
cat("Cleaned dataset dimensions: ", dim(dataset_clean4), "\n","\n")
Cleaned dataset dimensions:  845 49 
 
dataset <- dataset_clean4

#################################
## relationships

Q1 <- quantile(dataset$relationships, 0.25)
Q3 <- quantile(dataset$relationships, 0.75)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
library(dplyr)
dataset_clean5 <- dataset %>%
  filter(relationships >= lower_bound, relationships <= upper_bound)

cat("relationships","\n")
relationships 
cat("Original dataset dimensions: ", dim(dataset), "\n")
Original dataset dimensions:  845 49 
cat("Cleaned dataset dimensions: ", dim(dataset_clean5), "\n","\n")
Cleaned dataset dimensions:  795 49 
 
dataset <- dataset_clean5

#################################
## funding_rounds

Q1 <- quantile(dataset$funding_rounds, 0.25)
Q3 <- quantile(dataset$funding_rounds, 0.75)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
library(dplyr)
dataset_clean6 <- dataset %>%
  filter(funding_rounds >= lower_bound, funding_rounds <= upper_bound)

cat("funding_rounds","\n")
funding_rounds 
cat("Original dataset dimensions: ", dim(dataset), "\n")
Original dataset dimensions:  795 49 
cat("Cleaned dataset dimensions: ", dim(dataset_clean6), "\n","\n")
Cleaned dataset dimensions:  786 49 
 
dataset <- dataset_clean6

#################################
## funding_total_usd

Q1 <- quantile(dataset$funding_total_usd, 0.25)
Q3 <- quantile(dataset$funding_total_usd, 0.75)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
library(dplyr)
dataset_clean7 <- dataset %>%
  filter(funding_total_usd >= lower_bound, funding_total_usd <= upper_bound)

cat("funding_total_usd","\n")
funding_total_usd 
cat("Original dataset dimensions: ", dim(dataset), "\n")
Original dataset dimensions:  786 49 
cat("Cleaned dataset dimensions: ", dim(dataset_clean7), "\n","\n")
Cleaned dataset dimensions:  727 49 
 
dataset <- dataset_clean7

#################################
## milestones

Q1 <- quantile(dataset$milestones, 0.25)
Q3 <- quantile(dataset$milestones, 0.75)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
library(dplyr)
dataset_clean8 <- dataset %>%
  filter(milestones >= lower_bound, milestones <= upper_bound)

cat("milestones","\n")
milestones 
cat("Original dataset dimensions: ", dim(dataset), "\n")
Original dataset dimensions:  727 49 
cat("Cleaned dataset dimensions: ", dim(dataset_clean8), "\n","\n")
Cleaned dataset dimensions:  727 49 
 
dataset <- dataset_clean8

This code chunk removes outlier numerical values from a dataset, keeping only data points within the normal range in the dataset.

nrow(dataset)
[1] 727
ncol(dataset)
[1] 49

After removing outliers, there are 724 rows and 49 columns remaining in the dataset.

Numerical Attribute Boxplots After Removing Outliers:
library(ggplot2)

# Select numerical attributes for boxplots
numerical_attributes <- c("latitude", "longitude",
                           "age_first_funding_year", "age_last_funding_year",
                           "age_first_milestone_year", "age_last_milestone_year",
                           "relationships", "funding_rounds", "funding_total_usd",
                           "milestones", "avg_participants")

# Melt the dataset for easier plotting
melted_data <- reshape2::melt(dataset[, numerical_attributes])
No id variables; using all as measure variables
# Create boxplots with facet wrap
boxplot_plot <- ggplot(melted_data, aes(x = variable, y = value)) +
  geom_boxplot(fill = "darkgray", color = "black") +
  facet_wrap(~variable, scales = "free") +
  labs(title = "Boxplots for Numerical Attributes After Removing Outliers",
       x = "Attribute",
       y = "Value") +
  theme(plot.title = element_text(hjust = 0.5))

# Display the plot
print(boxplot_plot)

By removing the extreme values, the new range encompasses a larger spread of the data compared to the original range with outliers. The distribution appears more compact and less skewed. However, this change might not always lead to an increase in the actual data values or the total range but rather a change in the scale of the visualization due to the removal of the extreme values.

4.2-Data Integration

In data integration we are creating a unified view by combining information from diverse sources. Since all of our data is contained in one dataset and we are not adding two or more columns together, there is no need for the data integration step.

4.3-Data Reduction

In the data reduction step, we are enhancing efficiency and model performance by minimizing data size, focusing on relevant features, and mitigating the risk of overfitting. We will conduct the chi-squared test, check for duplication, and remove redundancy.

A) Elimination of Duplicates

Duplicate columns

names(dataset)
 [1] "Unnamed: 0"               "state_code"               "latitude"                 "longitude"                "zip_code"                 "id"                       "city"                    
 [8] "Unnamed: 6"               "name"                     "labels"                   "founded_at"               "closed_at"                "first_funding_at"         "last_funding_at"         
[15] "age_first_funding_year"   "age_last_funding_year"    "age_first_milestone_year" "age_last_milestone_year"  "relationships"            "funding_rounds"           "funding_total_usd"       
[22] "milestones"               "state_code.1"             "is_CA"                    "is_NY"                    "is_MA"                    "is_TX"                    "is_otherstate"           
[29] "category_code"            "is_software"              "is_web"                   "is_mobile"                "is_enterprise"            "is_advertising"           "is_gamesvideo"           
[36] "is_ecommerce"             "is_biotech"               "is_consulting"            "is_othercategory"         "object_id"                "has_VC"                   "has_angel"               
[43] "has_roundA"               "has_roundB"               "has_roundC"               "has_roundD"               "avg_participants"         "is_top500"                "status"                  

There are no duplicated columns as each attribute is unique.

Duplicate rows

sum(duplicated(dataset))
[1] 0

There is one duplicated row in the dataset that we must eliminate.

duplicated(dataset)
  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [32] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [63] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [94] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[125] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[156] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[187] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[218] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[249] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[280] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[311] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[342] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[373] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[404] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[435] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[466] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[497] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[528] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[559] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[590] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[621] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[652] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[683] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[714] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

The duplicated row is in row 653.

dataset[653, ]

From the table we can find the company name of the duplicate row to verify its duplication.

redwood_systems_rows <- dataset[dataset$name == "Redwood Systems", ]
print(redwood_systems_rows)

There are two rows dedicated to “Redwood Systems”. This shows that row 653 is indeed a duplicate. Therefore, we must eliminate it.

Remove Duplicated Rows:
dataset <- unique(dataset)

This code chunk removes duplicated rows.

Verify the Elimination of Duplicates:
sum(duplicated(dataset))
[1] 0

The duplicated row is removed.

B) Chi-squared Test

The chi-square test aids in feature selection by examining the independence between categorical attributes. It helps eliminate attributes that showcase a significant relationship between one another.

Relationship Between labels and status

contingency_table <- table(dataset$labels, dataset$status)
chi_square_result <- chisq.test(contingency_table)
print(chi_square_result)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 722.75, df = 1, p-value < 2.2e-16

The performed Chi-squared test demonstrates a substantial association between the ‘labels’ and ‘status’ columns. The obtained p-value, which is less than 2e-16, suggests a statistically significant correlation. Status and labels are dependent on one another. So, we can eliminate one of them. We choose to eliminate labels and keep status which is the class label. Labels might be the encoded version of status.

Relationship Between zip_code and city

contingency_table <- table(dataset$zip_code, dataset$city)
chi_square_result <- chisq.test(contingency_table)
Warning: Chi-squared approximation may be incorrect
print(chi_square_result)

    Pearson's Chi-squared test

data:  contingency_table
X-squared = 128324, df = 57905, p-value < 2.2e-16

This code chunk conducts the chi-squared test for attributes zip_code and city. With the results shown, we can interpret that zip_code and city have a significant correlation between them. In other words, they are dependent on one another. So, we can eliminate one of them. We chose to eliminate zip_code and keep city. Because city constitutes a part of the attribute Unnammed: 6, and if we want to delete column Unnamed: 6 we must keep city.

C) Elimination of Redundant Attributes

Before Attribute Elimination:
ncol(dataset)
[1] 49

There are 49 columns in the dataset.

names(dataset)
 [1] "Unnamed: 0"               "state_code"               "latitude"                 "longitude"                "zip_code"                 "id"                       "city"                    
 [8] "Unnamed: 6"               "name"                     "labels"                   "founded_at"               "closed_at"                "first_funding_at"         "last_funding_at"         
[15] "age_first_funding_year"   "age_last_funding_year"    "age_first_milestone_year" "age_last_milestone_year"  "relationships"            "funding_rounds"           "funding_total_usd"       
[22] "milestones"               "state_code.1"             "is_CA"                    "is_NY"                    "is_MA"                    "is_TX"                    "is_otherstate"           
[29] "category_code"            "is_software"              "is_web"                   "is_mobile"                "is_enterprise"            "is_advertising"           "is_gamesvideo"           
[36] "is_ecommerce"             "is_biotech"               "is_consulting"            "is_othercategory"         "object_id"                "has_VC"                   "has_angel"               
[43] "has_roundA"               "has_roundB"               "has_roundC"               "has_roundD"               "avg_participants"         "is_top500"                "status"                  

Unnamed: 0, latitude, longitude, zip_code, state_code.1, id, Unnamed: 6, and object_id are existing attributes in the dataset.

The chi-squared test showed us that zip_code and city are highly correlated. As a result, we can delete one of them and keep the other.

Attributes Unnamed: 0, id, Unnamed: 6, and object_id are irrelevant to this data mining task. As discussed in section 2, the original reference provides no justification on their usage. Therefore, they are unimportant and can be eliminated.

Latitude and longitude attributes are “accessory” attributes with no real impact on the dataset. Deleting them will simplify the later steps.

State_code and state_code.1 are duplicates. Only one has to remain to prevent redundancy.

Attribute(s) Keep Remove Why
Unnamed: 0 don’t keep Unnamed: 0 Irrelevant
state_code, state_code.1 state_code state_code.1 Duplicate attribute
latitude don’t keep latitude Unimportant
longitude don’t keep longitude Unimportant
zip_code, city city zip_code Dependent attributes (chi-squared test)
id don’t keep id Irrelevant
labels, status status labels Dependent attributes (chi-squared test)
Unnamed: 6, city, state_code.1 city, state_code.1 Unnamed:6 Redundant attribute
object_id don’t keep object_id Irrelevant
Redundant Attribute Elimination:
# Create a list of column names to remove
columns_to_remove <- c("Unnamed: 0", "state_code.1", "latitude", "longitude", "zip_code", "id", "Unnamed: 6", "labels", "object_id")

# Remove the specified columns from the dataset
dataset <- dataset[, !names(dataset) %in% columns_to_remove]

This code chunk removes all attributes redundant, irrelevant, and unimportant from the dataset.

After Attribute Elimination:
ncol(dataset)
[1] 40

There are 40 columns in the dataset.

names(dataset)
 [1] "state_code"               "city"                     "name"                     "founded_at"               "closed_at"                "first_funding_at"         "last_funding_at"         
 [8] "age_first_funding_year"   "age_last_funding_year"    "age_first_milestone_year" "age_last_milestone_year"  "relationships"            "funding_rounds"           "funding_total_usd"       
[15] "milestones"               "is_CA"                    "is_NY"                    "is_MA"                    "is_TX"                    "is_otherstate"            "category_code"           
[22] "is_software"              "is_web"                   "is_mobile"                "is_enterprise"            "is_advertising"           "is_gamesvideo"            "is_ecommerce"            
[29] "is_biotech"               "is_consulting"            "is_othercategory"         "has_VC"                   "has_angel"                "has_roundA"               "has_roundB"              
[36] "has_roundC"               "has_roundD"               "avg_participants"         "is_top500"                "status"                  

Unnamed: 0, latitude, longitude, zip_code, state_code.1, id, Unnamed: 6, and object_id do not exist in the dataset.

4.4-Data Transformation

In data transformation, we are preparing data for analysis and modeling through flooring, normalization, and encoding techniques.

A) Flooring Attributes

Flooring data, an essential aspect of data transformation, is crucial for various analytical and modeling processes. It facilitates the conversion of continuous numerical attributes into discrete values by rounding down to the nearest whole number. This technique is vital in simplifying complex numerical data, making it more manageable and easier to interpret.

Attributes Before Flooring:
# Selecting the specific columns for the first row
first_row <- dataset[1, c("age_first_funding_year", "age_last_funding_year",
                          "age_first_milestone_year", "age_last_milestone_year",
                          "relationships", "funding_rounds", "funding_total_usd",
                          "milestones", "avg_participants")]
# Printing the first row
print(first_row)

As shown above, the attributes contain continuous values before flooring.

Flooring:
# Columns to floor
cols_to_floor <- c("age_first_funding_year", "age_last_funding_year",
                   "age_first_milestone_year", "age_last_milestone_year",
                   "relationships", "funding_rounds", "funding_total_usd",
                   "milestones", "avg_participants")

# Applying floor to specified columns
dataset[cols_to_floor] <- lapply(dataset[cols_to_floor], floor)

This code chunk floors attributes from continuous to discrete numbers.

Attributes After Flooring:
# Selecting the specific columns for the first row
first_row <- dataset[1, c("age_first_funding_year", "age_last_funding_year",
                          "age_first_milestone_year", "age_last_milestone_year",
                          "relationships", "funding_rounds", "funding_total_usd",
                          "milestones", "avg_participants")]
# Printing the first row
print(first_row)

After flooring, the attributes are now discrete instead of continuous.

B) Normalization

We are going to normalize the numerical attribute funding_total_usd using min-max normalization. Numbers should fall between 0 and 1 (inclusive).

funding_total_usd Before Normalization:
# Selecting the specific columns for the first row
first_row <- dataset[1, c("funding_total_usd")]
# Printing the first row
print(first_row)

The table above shows an unnormalized value from the funding_total_usd attribute.

Normalization:
normalize <- function(x) {return ((x - min(x)) / (max(x) - min(x)))}
dataset$funding_total_usd<-normalize(dataset$funding_total_usd)

This code chunk normalizes attribute funding_total_usd using min-max normalization.

funding_total_usd After Normalization:
# Selecting the specific columns for the first row
first_row <- dataset[1, c("funding_total_usd")]
# Printing the first row
print(first_row)

The table above shows a normalized value from the funding_total_usd attribute.

min_value <- min(dataset$funding_total_usd)
max_value <- max(dataset$funding_total_usd)

# Print the results with labels
cat("The min is:", min_value, "\n")
The min is: 0 
cat("The max is:", max_value)
The max is: 1

In the min-max normalization of attribute funding_total_usd, the minimum is 0 while the max is 1.

# Find row index for minimum and maximum funding_total_usd
min_row <- which.min(dataset$funding_total_usd)
max_row <- which.max(dataset$funding_total_usd)

# Print rows with minimum and maximum funding_total_usd along with name and status
print(dataset[min_row, c('name', 'funding_total_usd', 'status')])

print(dataset[max_row, c('name', 'funding_total_usd', 'status')])

In both tables, we can verify that the min-max normalization was successful. The first table shows the row with minimum normalization and the second table shows the row with maximum normalization.

C) Encoding

Here, we will encode attributes to simplify analysis. The attributes to encode are:

  1. Date attributes: founded_at, closed_at, first_funding_at, last_funding_at

  2. Class label: status

  3. Categorical attributes: state_code, category_code, and city

  4. Unique attribute: name

Attributes Before Encoding:
dataset[3, c("state_code", "city", "name", "founded_at", "closed_at", "first_funding_at", "last_funding_at", "category_code", "status")]

Attributes appearing in their original format.

i) Encoding Date Attributes: founded_at, closed_at, first_funding_at, last_funding_at

dataset$founded_at <- gsub("/", "", dataset$founded_at)
dataset$closed_at <- gsub("/", "", dataset$closed_at)
dataset$first_funding_at <- gsub("/", "", dataset$first_funding_at)
dataset$last_funding_at <- gsub("/", "", dataset$last_funding_at)

dataset$founded_at <- substr(dataset$founded_at, nchar(dataset$founded_at) - 3, nchar(dataset$founded_at))
dataset$closed_at <- substr(dataset$closed_at, nchar(dataset$closed_at) - 3, nchar(dataset$closed_at))
dataset$first_funding_at <- substr(dataset$first_funding_at, nchar(dataset$first_funding_at) - 3, nchar(dataset$first_funding_at))
dataset$last_funding_at <- substr(dataset$last_funding_at, nchar(dataset$last_funding_at) - 3, nchar(dataset$last_funding_at))

dataset$founded_at <- as.numeric(dataset$founded_at)
dataset$closed_at <- as.numeric(dataset$closed_at)
Warning: NAs introduced by coercion
dataset$first_funding_at <- as.numeric(dataset$first_funding_at)
dataset$last_funding_at <- as.numeric(dataset$last_funding_at)

This code chunk converts attributes founded_at, closed_at, first_funding_at, last_funding_at from date format mm/dd/yyyy to numerical numbers.

ii) Encoding Categorical Attributes: state_code, category_code, and city

library(caret)
Loading required package: lattice
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
library(dplyr)
library(lattice)

# Columns to be encoded
attributes_to_encode <- c("state_code", "category_code", "city")

# Loop through each attribute for encoding
for (attribute in attributes_to_encode) {
  # Use caret's method for encoding
  dataset[[attribute]] <- as.factor(dataset[[attribute]])
  dataset[[attribute]] <- as.numeric(dataset[[attribute]])
}

This code chunk creates from each of the selected columns a new column that holds a frequency-encoded version of the attributes values. Rows that share the same value of the attribute have the same encoding.

iii) Ecoding Unique Attribute: name

# Replace each unique category with a numerical label
dataset$name <- as.numeric(factor(dataset$name, levels = unique(dataset$name)))

Creates a new column for the encoded unique values of the attribute name.

iv) Encoding Class Label: status

dataset$status <- ifelse(dataset$status == "acquired", 1, 0)

Encodes status attribute to 1 for acquired status and 0 for closed status.

Attributes After Encoding:
dataset[3, c("state_code", "city", "name", "founded_at", "closed_at", "first_funding_at", "last_funding_at", "category_code", "status")]

Attributes founded_at, closed_at, first_funding_at, and last_funding_at appear in the encoded ( mm-yyyy ) form. Attribute status appear in 1 (for acquired) and 0 (for closed) form.

Downloading the Pre-processed Dataset

library(writexl)

# Assuming your preprocessed dataset is named 'dataset'
write_xlsx(dataset, path = "Preprocessed_StartupData.xlsx")
preprocessed_dataset <- read_excel("Preprocessed_StartupData.xlsx")

To save the pre-processing work and use it later in classification and clustering.

Comparison of Attributes Before vs After Pre-processing

After preprocessing, the data undergoes transformation that can include cleaning, normalization, encoding, and more, resulting in a refined dataset more amenable to analysis, reducing noise, and enhancing the accuracy of the learningmodels.

First, we will check the number or columns and attributes after pre-processing.

# Number of columns
num_cols <- ncol(dataset)
# Number of attributes
num_attrs <- nrow(dataset)

# Print the values
cat("Number of columns:", num_cols, "\n")
Number of columns: 40 
cat("Number of attributes:", num_attrs, "\n")
Number of attributes: 727 

There are 40 columns and 727 rows.

Then, we will observe changes occurred to numerical, nominal, and binary attributes after pre-processing.

A) Assessing Class Label Balance After Pre-processing

library(ggplot2)
library(grid)

# Create a bar plot for the "status" attribute
gg <- ggplot(dataset, aes(x = status)) +
  geom_bar(fill = "darkgray", color = "black") +
  labs(title = "Distribution of Startup Status", x = "Status", y = "Count") +
  theme(plot.title = element_text(hjust = 0.5))

# Print the plot
print(gg)

# Add an external annotation to the right side
grid.text("1 = Acquired\n0 = Closed", x = 0.2, y = 0.92, just = c("right", "top"), gp = gpar(fontsize = 12, col = "black"))

Despite pre-processing, there is still a class imbalance in the class label (status).

B) Numerical Attributes Before vs After Pre-processing

# Load the required libraries
library(ggplot2)

# Select numerical attributes for histograms
numerical_attributes <- c("age_first_funding_year", "age_last_funding_year",
                           "age_first_milestone_year", "age_last_milestone_year",
                           "relationships", "funding_rounds", "funding_total_usd",
                           "milestones", "avg_participants")

# Melt the dataset for easier plotting
melted_data <- reshape2::melt(dataset[, numerical_attributes])
No id variables; using all as measure variables
# Create histogram with facet wrap
histogram_plot_numerical_after <- ggplot(melted_data, aes(x = value)) +
  geom_histogram(binwidth = 1, fill = "darkgray", color = "black") +
  facet_wrap(~variable, scales = "free") +
  labs(title = "Histogram for Numerical Attributes AFTER Pre-processing", x = "Value", y = "Frequency") +
  theme(plot.title = element_text(hjust = 0.5))

# Set up a 1x2 plotting grid to show histograms side by side
par(mfrow = c(1, 2))

# Plot the histograms side by side
plot(histogram_plot_numerical_before)

plot(histogram_plot_numerical_after)

The graphs above show a comparison between numerical data BEFORE and AFTER pre-processing. Many alterations have taken place within the numerical attributes. From flooring, normalization, and removing outliers.

C) Nominal Attributes Before vs After Pre-processing

library(ggplot2)
library(tidyr)

# Selecting specific columns for visualization
columns_to_visualize <- dataset[, c("state_code", "city", "category_code")]

# Melt the required columns for visualization
melted_data_before <- gather(data = columns_to_visualize)

# Plotting bar graphs for the specified attributes and facet_wrap
barplot_nominal_after <- ggplot(melted_data_before, aes(x = value, fill = key)) +
  geom_bar(position = "dodge", stat = "count", color = "black", fill = "darkgray") +
  facet_wrap(~key, scales = "free") +
  labs(title = "Nominal Attributes AFTER Pre-processing", x = "Values", y = "Count") +
  theme(plot.title = element_text(hjust = 0.5))

# Displaying the plot
print(barplot_nominal_before)

print(barplot_nominal_after)

Many alterations have taken place within the nominal attributes. Attributes were encoded and outliers were removed. In addition, attribute state_code.1 was deemed redundant and hence removed. From the graphs, we can observe frequent trends in category_code, city, and state_code.

D) Binary Attributes Before vs After Pre-processing

library(ggplot2)
library(tidyr)

# List of binary attributes
binary_attributes <- c("is_CA", "is_NY", "is_MA", "is_TX", "is_otherstate", 
                       "is_software", "is_web", "is_mobile", "is_enterprise", 
                       "is_advertising", "is_gamesvideo", "is_ecommerce", 
                       "is_biotech", "is_consulting", "is_othercategory", 
                       "has_VC", "has_angel", "has_roundA", "has_roundB", 
                       "has_roundC", "has_roundD", "is_top500", "status")

# Melt the datasets for easier plotting
melted_data <- gather(dataset, key = "variable", value = "value", all_of(binary_attributes))

# Create bar plots with facet wrap
barplot_binary_after <- ggplot(melted_data, aes(x = value, fill = variable)) +
  geom_bar(position = "dodge", stat = "count", color = "black", fill = "darkgray") +
  facet_wrap(~variable, scales = "free") +
  labs(title = "Bar Plots for Binary Attributes AFTER Pre-processing", x = "Value", y = "Count") +
  theme(plot.title = element_text(hjust = 0.5))

# Display the plot
print(barplot_binary_before)

print(barplot_binary_after)

The binary attributes have remained unaltered; no changes have been applied.

Plotting of Pre-processed Data

Plotting pre-processed data is crucial as it visually unveils patterns, trends, and distributions within the dataset. It helps understand attribute distributions, correlations, and spot variations post-preprocessing, which is fundamental for making informed decisions and uncovering insights in the data analysis process.

A) Relationship between Top 500 Companies and Status

cor(dataset$is_top500, dataset$status)
[1] 0.3148977

A correlation coefficient of 0.315 suggests a moderate positive correlation between the attributes ‘is_top500’ and the class label ‘status’. This implies that changes in one variable are associated with relatively proportional changes in the other variable, albeit not perfectly.

library(ggplot2)

# Create a summary table to count the combinations of is_top500 and status
summary_table <- table(dataset$is_top500, dataset$status)

# Convert the summary table to a data frame
summary_df <- as.data.frame(summary_table)

# Rename the columns for clarity
colnames(summary_df) <- c("is_top500", "status", "count")

# Create a barplot
ggplot(summary_df, aes(x = is_top500, y = count, fill = status)) +
  geom_bar(stat = "identity", position = "dodge", aes(fill = status), color = "black") +
  labs(title = "Top 500 Company vs. Status", x = "Top 500", y = "Count") + 
  theme(plot.title = element_text(hjust = 0.5)) +
  scale_fill_manual(values = c("acquired" = "darkgray", "closed" = "darkgray"))

Is being a Top 500 company a strong indicator of whether a startup will be acquired or closed? The vast majority of top 500 companies are acquired companies.

B) Distribution of Startup Sectors

library(ggplot2)
library(scales)

# Define shades of gray
colors <- c("#FFFFFF", "#F9F9F9", "#F2F2F2", "#E5E5E5", "#D9D9D9", "#CCCCCC", "#B2B2B2", "#999999", "#808080", "#666666") 

# Create a data frame for your binary attributes
binary_data <- data.frame(
  Attribute = c("is_software", "is_web", "is_mobile", "is_enterprise", "is_advertising", "is_gamesvideo", "is_ecommerce", "is_biotech", "is_consulting", "is_othercategory"),
  Value = c(sum(dataset$is_software), sum(dataset$is_web), sum(dataset$is_mobile), sum(dataset$is_enterprise), sum(dataset$is_advertising), sum(dataset$is_gamesvideo), sum(dataset$is_ecommerce), sum(dataset$is_biotech), sum(dataset$is_consulting), sum(dataset$is_othercategory))
)

# Calculate percentages
binary_data$Percentage <- (binary_data$Value / sum(binary_data$Value)) * 100

# Create the pie chart with shades of gray
pie_chart <- ggplot(binary_data, aes(x = "", y = Percentage, fill = Attribute)) +
  geom_bar(stat = "identity", width = 1, color="black") +
  coord_polar(theta = "y") +
  labs(title = "Startup Category") +
  scale_fill_manual(values = colors) +  # Set the colors
  scale_y_continuous(labels = percent_format(scale = 1))

# Display the pie chart
print(pie_chart)

What are the most popular startup sectors? The answer is software. Most startups focus on tech-related sectors, like software, web, and mobile.

C) Distribution of Startup State of Origin

library(ggplot2)

# Create a data frame with the counts of each binary attribute
binary_data <- data.frame(
  Attribute = c("is_CA", "is_NY", "is_MA", "is_TX", "is_otherstate"),
  Count = c(
    sum(dataset$is_CA),
    sum(dataset$is_NY),
    sum(dataset$is_MA),
    sum(dataset$is_TX),
    sum(dataset$is_otherstate)
  )
)

# Calculate percentages
binary_data$Percentage <- (binary_data$Count / sum(binary_data$Count)) * 100

# Create a pie chart
pie_chart <- ggplot(binary_data, aes(x = "", y = Percentage, fill = Attribute)) +
  geom_bar(stat = "identity", width = 1, color="black") +
  coord_polar(theta = "y") +  # Convert to polar coordinates for a pie chart
  labs(title = "Distribution of Startup State of Origin") +
  scale_fill_manual(values = c("is_CA" = "lightgray", "is_NY" = "darkgray", "is_MA" = "darkgray", "is_TX" = "darkgray", "is_otherstate" = "darkgray")) +
  theme_minimal() +  
  geom_text(aes(label = paste0(round(Percentage, 1), "%")), position = position_stack(vjust = 0.5))

# Display the pie chart
print(pie_chart)

Which state is the most popular choice for startups to launch in? The answer is California. 51.4% of all startups launched from California.

D) Distribution of Funding Rounds and Status

library(ggplot2)
library(cowplot)
library(tidyr)  # Load the tidyr package

# Filter rows where status is "acquired"
acquired_data <- subset(dataset, status == "1")

# Create a long-format dataset for use with ggplot2
acquired_data_long <- tidyr::gather(acquired_data, key = "Attribute", value = "BinaryValue", has_VC, has_angel, has_roundA, has_roundB, has_roundC, has_roundD)

# Create histograms with facet_wrap for "acquired" status
plot1 <- ggplot(acquired_data_long, aes(x = BinaryValue)) +
  geom_histogram(binwidth = 1, fill = "darkgray", color = "black") +
  facet_wrap(~ Attribute, scales = "free_x") +
  labs(title = "'Acquired' Status", x = "Value", y = "Frequency") +
  theme(plot.title = element_text(hjust = 0.5))

# Filter rows where status is "closed"
closed_data <- subset(dataset, status == "0")

# Create a long-format dataset for use with ggplot2
closed_data_long <- tidyr::gather(closed_data, key = "Attribute", value = "BinaryValue", has_VC, has_angel, has_roundA, has_roundB, has_roundC, has_roundD)

# Create histograms with facet_wrap for "closed" status
plot2 <- ggplot(closed_data_long, aes(x = BinaryValue)) +
  geom_histogram(binwidth = 1, fill = "darkgray", color = "black") +
  facet_wrap(~ Attribute, scales = "free_x") +
  labs(title = "'Closed' Status", x = "Value", y = "Frequency") +
  theme(plot.title = element_text(hjust = 0.5))

# Plotting the two histograms side by side
plot_grid(plot1, plot2, nrow = 1)

Do increased funding rounds serve as an indicator of whether a startup is more likely to be acquired or closed in the future? The answer is not necessarily, but from the graphs we can see that the number of acquired startups that went through series A, B, C, and D is more than the number of closed startups that went through them. The majority of “acquired” startups had a series A funding round, while the majority of “closed” startups did not. Series A and Series B funding could be a potential indicator of startup success. Angel investors invested almost equally in both “acquired” and “closed” startups. VCs tend to invest more in startups that become “acquired” in the future. Nevertheless, the results may be inaccurate because the class label is imbalanced.

E) Relationship Between the Number of Funding Rounds and Total Funding Raised

range(dataset$funding_total_usd)
[1] 0 1

Attribute funding_total_usd was normalized using min-max normalization. So, the range of funding_total_usd values fall within 0 (minimum) and 1 (maximum).

range(dataset$funding_rounds)
[1] 1 6

The least amount of funding rounds went through by any startup is 1, indicating that all startups at least went through one funding round. The most amount of funding rounds went through by any startup is 6.

cor(dataset$funding_rounds, dataset$funding_total_usd)
[1] 0.4391601

A correlation of 0.439 indicates a moderate positive correlation between the variables ‘funding_rounds’ and ‘funding_total_usd’.

boxplot(dataset$funding_total_usd ~ dataset$funding_rounds,
        xlab = "Number of Funding Rounds",
        ylab = "Funding Total (USD)",
        main = "Funding Rounds vs Funding Total",
        col = "darkgray")

Is there a strong correlation between the number of funding rounds and the total funding received by a company? It’s generally true, but not an absolute rule. There’s often a positive relationship between increased funding rounds and raised funds, indicating that more rounds tend to yield more money.

F) Association Between Total Funding and Startup Category

library(ggplot2)
library(dplyr)
library(tidyr)

# Summary table: Sum of funding for each category
category_funding_summary <- dataset %>%
  group_by(is_software, is_web, is_mobile, is_enterprise,
           is_advertising, is_gamesvideo, is_ecommerce, is_biotech, is_consulting, is_othercategory) %>%
  summarise(total_funding = sum(funding_total_usd))
`summarise()` has grouped output by 'is_software', 'is_web', 'is_mobile', 'is_enterprise', 'is_advertising', 'is_gamesvideo', 'is_ecommerce', 'is_biotech', 'is_consulting'. You can override using the `.groups` argument.
# Reshape the data for plotting
category_funding_summary <- category_funding_summary %>%
  pivot_longer(
    cols = -total_funding,
    names_to = "Category",
    values_to = "BinaryValue"
  )

# Create a bar plot with gray colors
ggplot(category_funding_summary, aes(x = Category, y = total_funding, fill = BinaryValue)) +
  geom_bar(stat = "identity") +
  labs(title = "Total Funding vs Startup Category", x = "Category", y = "Total Funding") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5)) 

Is there a strong correlation between the sector of the company and the total funding received by a company? The answer is yes. Software startups received the highest amounts of funding. Followed by other tech-related sectors: web and mobile.


5-Data Mining Techniques

To identify patterns and make predictions based on the data of the dataset, we are going to perform the data mining techniques classification and clustering. Classification is a supervised learning technique that uses labeled data to train a model to predict the class of new data points. Clustering is an unsupervised learning technique that groups unlabeled data points into clusters based on their similarities.

5.1-Classification

As mentioned before, classification is a form of supervised learning, where the class label is known beforehand. In the case of startup data, the class label is the binary attribute “status”. Status attribute holds two values, either 1 for “acquired” status, or 0 for “closed” status. In classification, the algorithm learns from labeled training data, then establishes a relationship between input features and their respective classes. The learned patterns are then used to classify new, unseen data.

In this project, we are going to use the decision tree algorithm to perform classification. Decision tree is considered a greedy algorithm. The tree is constructed in a top-down recursive divide-and-conquer manner. It takes the form of a branching tree; the top node is the decision node and the bottom unbranched leaves represent the class.

The following classification steps will be performed:

  1. Construct a model using training data.
  2. Evaluate the model using testing data.
  3. Predict the class using new data.

Partitioning Method

We will use the hold-out method as the partitioning method. The dataset will be sectioned into three partitions: 70:30 data split, 80:20 data split, and 90:10 data splits. The hould-out split method is a common practice in machine learning. It helps in maximizing the usage of available data for both training and testing. By performing this method, we can prevent an overfitted model by providing a separate data set for model evaluation.For each split we will run Information Gain, Gain Ration, and Gini Index. Ultimately, we will have a total of nine decision trees.

◆ Information Gain is is a measure used in the decision tree algorithm. It represents the amount of disorder or uncertainty in a dataset that is reduced (or gained) after splitting the data on an attribute. It measures the reduction in entropy (impurity or disorder) after the dataset is split. Attribute with the maximum information gain is selected as the decision node.

◆ Gain Ratio is considers the Information Gain but normalizes it by the intrinsic information associated with each split. It accounts for the size of the branches resulting from a split. Attribute with the maximum gain ratio is selected as the splitting attribute. Attribute with the maximum gain ratio is selected as the splitting attribute.

◆ Gini Index is is a measure of impurity or the quality of a split in a dataset. In the context of decision trees, it calculates the overall probability of a specific feature being misclassified. Attribute with the minimum gini index is selected as the splitting attribute.

Before we start let’s take a look at the startup data after pre-processing:

library(readxl)
preprocessed_dataset <- read_excel("Preprocessed_StartupData.xlsx")
str(preprocessed_dataset)
tibble [727 × 40] (S3: tbl_df/tbl/data.frame)
 $ state_code              : num [1:727] 2 2 2 2 2 2 2 2 2 11 ...
 $ city                    : num [1:727] 143 90 143 45 144 105 105 120 98 91 ...
 $ name                    : num [1:727] 1 2 3 4 5 6 7 8 9 10 ...
 $ founded_at              : num [1:727] 2007 2000 2009 2002 2010 ...
 $ closed_at               : num [1:727] NA NA NA NA 2012 ...
 $ first_funding_at        : num [1:727] 2009 2005 2010 2005 2010 ...
 $ last_funding_at         : num [1:727] 2010 2009 2010 2007 2012 ...
 $ age_first_funding_year  : num [1:727] 2 5 1 3 0 4 1 1 1 4 ...
 $ age_last_funding_year   : num [1:727] 3 9 1 5 1 4 5 4 5 4 ...
 $ age_first_milestone_year: num [1:727] 4 7 1 6 0 5 3 2 0 3 ...
 $ age_last_milestone_year : num [1:727] 6 7 2 6 0 5 6 6 4 4 ...
 $ relationships           : num [1:727] 3 9 5 5 2 3 6 14 8 0 ...
 $ funding_rounds          : num [1:727] 3 4 1 3 2 1 3 3 5 1 ...
 $ funding_total_usd       : num [1:727] 0.00771 0.84921 0.05484 0.84709 0.0273 ...
 $ milestones              : num [1:727] 3 1 2 1 1 1 2 4 2 0 ...
 $ is_CA                   : num [1:727] 1 1 1 1 1 1 1 1 1 0 ...
 $ is_NY                   : num [1:727] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_MA                   : num [1:727] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_TX                   : num [1:727] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_otherstate           : num [1:727] 0 0 0 0 0 0 0 0 0 1 ...
 $ category_code           : num [1:727] 20 9 34 31 12 21 31 34 34 34 ...
 $ is_software             : num [1:727] 0 0 0 1 0 0 1 0 0 0 ...
 $ is_web                  : num [1:727] 0 0 1 0 0 0 0 1 1 1 ...
 $ is_mobile               : num [1:727] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_enterprise           : num [1:727] 0 1 0 0 0 0 0 0 0 0 ...
 $ is_advertising          : num [1:727] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_gamesvideo           : num [1:727] 0 0 0 0 1 0 0 0 0 0 ...
 $ is_ecommerce            : num [1:727] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_biotech              : num [1:727] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_consulting           : num [1:727] 0 0 0 0 0 0 0 0 0 0 ...
 $ is_othercategory        : num [1:727] 1 0 0 0 0 1 0 0 0 0 ...
 $ has_VC                  : num [1:727] 0 1 0 0 1 0 1 1 1 1 ...
 $ has_angel               : num [1:727] 1 0 0 0 1 0 0 1 1 0 ...
 $ has_roundA              : num [1:727] 0 0 1 0 0 0 1 1 1 0 ...
 $ has_roundB              : num [1:727] 0 1 0 1 0 1 1 0 0 0 ...
 $ has_roundC              : num [1:727] 0 1 0 1 0 0 0 0 0 0 ...
 $ has_roundD              : num [1:727] 0 1 0 1 0 0 0 0 0 0 ...
 $ avg_participants        : num [1:727] 1 4 4 3 1 3 1 1 1 1 ...
 $ is_top500               : num [1:727] 0 1 1 1 1 1 1 1 1 0 ...
 $ status                  : num [1:727] 1 1 1 1 0 0 1 1 0 0 ...

All attributes have been changed to a numerical format to ensure smooth utilization of classification tree plotting functions.

Balancing the Class Label

# Identifying the rows of each status
closed_indices <- which(preprocessed_dataset$status == 0)
acquired_indices <- which(preprocessed_dataset$status == 1)

# Number of samples for each status class
num_closed <- length(closed_indices)
num_acquired <- length(acquired_indices)

# Subsampling to balance the data
if (num_closed > num_acquired) {
  # Subsample the "closed" status to match the "acquired" count
  sampled_closed_indices <- sample(closed_indices, num_acquired)
  balanced_preprocessed_dataset <- rbind(preprocessed_dataset[sampled_closed_indices, ], preprocessed_dataset[acquired_indices, ])
} else {
  # Subsample the "acquired" status to match the "closed" count
  sampled_acquired_indices <- sample(acquired_indices, num_closed)
  balanced_preprocessed_dataset <- rbind(preprocessed_dataset[closed_indices, ], preprocessed_dataset[sampled_acquired_indices, ])
}

We have previously shown the imbalance of the class label “status” (section 4: Assessing Class Label Balance After Pre-processing). To address the class imbalance issue, we will use the undersampling technique. Undersampling is a balancing method used in machine learning where you reduce the number of instances of the over-represented class (status = “acquired” or 1) to make it equal to the number of instances of the under-represented class (status = “closed” or 0). By balancing the class distribution, undersampling can lead to more robust and fair models, preventing the model from being overly influenced by the majority class.

Verify Class Label Balance
# Count of each class in the original preprocessed_dataset
original_class_counts <- table(preprocessed_dataset$status)

# Count of each class in the balanced preprocessed_dataset
balanced_class_counts <- table(balanced_preprocessed_dataset$status)

# Display the counts
print("Original preprocessed_dataset Class Counts:")
[1] "Original preprocessed_dataset Class Counts:"
print(original_class_counts)

  0   1 
275 452 
print("Balanced preprocessed_dataset Class Counts:")
[1] "Balanced preprocessed_dataset Class Counts:"
print(balanced_class_counts)

  0   1 
275 275 

There are 452 rows for acquired status (majority), and 275 rows for closed status (minority) before balancing. After balancing, there are 275 rows for acquired status and 275 rows for closed status showcasing a balanced class label ready for classification.

Model Evaluation Metrics

# Function for Model Evaluation
evaluate_model <- function(predictions, actual_labels) {
  # Confusion Matrix
  confusion_matrix <- table(actual_labels, predictions)
  print(confusion_matrix)
  
  TP <- confusion_matrix[1, 1]
  TN <- confusion_matrix[2, 2]
  FP <- confusion_matrix[2, 1]
  FN <- confusion_matrix[1, 2]
  
  # Calculate metrics
  accuracy <- ((TP + TN) / sum(confusion_matrix)) * 100
  precision <- (TP / (TP + FP)) * 100
  sensitivity <- (TP / (TP + FN)) * 100
  specificity <- (TN / (TN + FP)) * 100
  
  # Print metrics
  cat("Accuracy:", accuracy, "%\n")
  cat("Precision:", precision, "%\n")
  cat("Sensitivity (Recall):", sensitivity, "%\n")
  cat("Specificity:", specificity, "%\n")
  
  # Return a list of metrics
  return(list(
    accuracy = accuracy,
    precision = precision,
    sensitivity = sensitivity,
    specificity = specificity
  ))
}

install.packages("caret")
trying URL 'https://cran.rstudio.com/bin/macosx/big-sur-arm64/contrib/4.3/caret_6.0-94.tgz'
Content type 'application/x-gzip' length 3586369 bytes (3.4 MB)
==================================================
downloaded 3.4 MB

The downloaded binary packages are in
    /var/folders/tc/srw9q4xd5nv7vtcjswsrg2b00000gn/T//RtmpwDBGoy/downloaded_packages
install.packages("pROC")
trying URL 'https://cran.rstudio.com/bin/macosx/big-sur-arm64/contrib/4.3/pROC_1.18.5.tgz'
Content type 'application/x-gzip' length 1128880 bytes (1.1 MB)
==================================================
downloaded 1.1 MB

The downloaded binary packages are in
    /var/folders/tc/srw9q4xd5nv7vtcjswsrg2b00000gn/T//RtmpwDBGoy/downloaded_packages
library(caret)
Loading required package: ggplot2
Loading required package: lattice
library(pROC)
Type 'citation("pROC")' for a citation.

Attaching package: ‘pROC’

The following objects are masked from ‘package:stats’:

    cov, smooth, var

The code encapsulates a function that facilitates the evaluation of classification models. The function calculates and prints common classification metrics (accuracy, precision, sensitivity, and specificity) based on predicted and actual labels.

Decision Trees

Presented are two tree models: simplified and original. In decision tree models, the attribute demonstrating the most significant information gain is represented as the decision node or the root. Upon inspection of the displayed trees, it’s apparent that the ‘relationships’ attribute possesses the highest information gain, positioned prominently at the top of the tree structure.

1-First Partition: (70% training, 30% testing)

Partitioning

set.seed(1234)
ind=sample(2, nrow(balanced_preprocessed_dataset), replace=TRUE, prob=c(0.70 , 0.30))
train_data_70=balanced_preprocessed_dataset[ind==1,]
test_data_70=balanced_preprocessed_dataset[ind==2,]

The code splits the preprocessed dataset into 70% training data and 30% testing data.

Verify Partitioning

dim(train_data_70)
[1] 381  40
dim(test_data_70)
[1] 169  40

The training data consist of 381 rows. The testing data consist of 169 rows.

A) ID3 Algorithm: Information Gain

A.1) Constructing the Decision Tree

library(party)
Loading required package: grid
Loading required package: mvtnorm
Loading required package: modeltools
Loading required package: stats4
Loading required package: strucchange
Loading required package: zoo

Attaching package: ‘zoo’

The following objects are masked from ‘package:base’:

    as.Date, as.Date.numeric

Loading required package: sandwich
library(mvtnorm)
library(modeltools)
library(stats4)
library(strucchange)
library(zoo)

myFormula <- status~ state_code + city + name + founded_at + closed_at + first_funding_at + last_funding_at + 
            age_first_funding_year + age_last_funding_year + age_first_milestone_year + age_last_milestone_year + 
            relationships + funding_rounds + funding_total_usd + milestones + is_CA + is_NY + is_MA + is_TX + is_otherstate + 
            category_code + is_software + is_web + is_mobile + is_enterprise + is_advertising + is_gamesvideo + is_ecommerce + 
            is_biotech + is_consulting + is_othercategory + has_VC + has_angel + has_roundA + has_roundB + has_roundC + 
            has_roundD + avg_participants + is_top500

preprocessed_dataset_ctree_IG_70<-ctree(myFormula, data=train_data_70)

table(predict(preprocessed_dataset_ctree_IG_70), train_data_70$status)
                   
                      0   1
  0                  44   0
  0.263157894736842  42  15
  0.525              95 105
  0.775              18  62

The code builds the decision tree model.

A.2) Plotting

print(preprocessed_dataset_ctree_IG_70)

     Conditional inference tree with 4 terminal nodes

Response:  status 
Inputs:  state_code, city, name, founded_at, closed_at, first_funding_at, last_funding_at, age_first_funding_year, age_last_funding_year, age_first_milestone_year, age_last_milestone_year, relationships, funding_rounds, funding_total_usd, milestones, is_CA, is_NY, is_MA, is_TX, is_otherstate, category_code, is_software, is_web, is_mobile, is_enterprise, is_advertising, is_gamesvideo, is_ecommerce, is_biotech, is_consulting, is_othercategory, has_VC, has_angel, has_roundA, has_roundB, has_roundC, has_roundD, avg_participants, is_top500 
Number of observations:  381 

1) relationships <= 2; criterion = 1, statistic = 59.945
  2) is_top500 <= 0; criterion = 0.991, statistic = 13.464
    3)*  weights = 44 
  2) is_top500 > 0
    4)*  weights = 57 
1) relationships > 2
  5) relationships <= 8; criterion = 0.993, statistic = 14.107
    6)*  weights = 200 
  5) relationships > 8
    7)*  weights = 80 
plot(preprocessed_dataset_ctree_IG_70)

plot(preprocessed_dataset_ctree_IG_70,type="simple")

Root Node (Node 1):
  • Splitting attribute: “relationships” <= 3.
  • The algorithm considers the “relationships” feature for the first decision.

Branches:
  • Node 2: If “relationships” <= 3, it evaluates the next criterion.
  • Node 7: If “relationships” > 3, it evaluates a different criterion.

Leaf Nodes (Terminal Nodes):
  • Node 4, Node 5, Node 6, Node 9, Node 10, Node 11: Terminal nodes where the decision tree makes predictions.

Number of Nodes: 7 (1 root, 4 internal, 2 leaf)

Most Important Features:
  1. relationships: Critical as the root node, indicating high importance.
  2. is_top500: Significant for further splits, showcasing its relevance.
  3. age_last_milestone_year: Used for additional splits, emphasizing its contribution.

In summary, this tree utilizes the “relationships” feature for the initial split, followed by additional criteria at each branching point, ultimately leading to predictions at the terminal nodes. The weights associated with each terminal node indicate the number of observations falling into each category.

A.3) Testing

predictions_IG_70 <- predict(preprocessed_dataset_ctree_IG_70, newdata = test_data_70, type = "response")
labels_IG_70 <- test_data_70$status

The code generates predictions for the test dataset using the decision tree model.

A.4) Confusion Matrix

confusion_matrix_IG_70 <- table(test_data_70$status, predictions_IG_70)
print(confusion_matrix_IG_70)
   predictions_IG_70
     0 0.263157894736842 0.525 0.775
  0 12                23    35     6
  1  0                 7    48    38

The confusion matrix for the provided code indicates the following:

  • True Positive (TP): 48 cases were correctly predicted as 0.775.
  • False Positive (FP): 35 cases were incorrectly predicted as 0.525, 6 cases as 0.775, and 23 cases as 0.2632.
  • True Negative (TN): 12 cases were correctly predicted as 0.2632, 7 cases as 0.525, and 38 cases as 0.775.
  • False Negative (FN): 0 cases were incorrectly predicted as 0.2632.

A.5) Evaluation Metrics

metrics_IG_70 <- evaluate_model(predictions_IG_70, labels_IG_70)
             predictions
actual_labels  0 0.263157894736842 0.525 0.775
            0 12                23    35     6
            1  0                 7    48    38
Accuracy: 11.2426 %
Precision: 100 %
Sensitivity (Recall): 34.28571 %
Specificity: 100 %
print(metrics_IG_70)
$accuracy
[1] 11.2426

$precision
[1] 100

$sensitivity
[1] 34.28571

$specificity
[1] 100

Accuracy (11.2%): This metric represents the overall correctness of the model’s predictions. In this case, the model achieved an accuracy of 15.4%, indicating that only a small percentage of predictions were correct.
Precision (100 %): Precision measures the accuracy of positive predictions. In this context, the model achieved a precision of 84.2%, indicating that when it predicted a positive outcome, it was correct in 84.2% of cases.
Sensitivity (34.2 %): Sensitivity, also known as recall, measures the ability of the model to correctly identify positive instances. In this case, the model’s sensitivity is 32%, suggesting that it didn’t perform well in capturing all positive instances.
Specificity (100 %): Specificity measures the ability of the model to correctly identify negative instances. With a specificity of 76.9%, the model performed relatively well in accurately identifying negative cases.

In summary, while the model exhibited high precision and specificity, the low accuracy and sensitivity signal potential challenges, especially in correctly identifying positive instances.

B) C50 Algorithm: Gain Ratio

B.1) Constructing the Decision Tree

library(C50)

# Convert 'status' to a factor
train_data_70$status <- as.factor(train_data_70$status)

# Train the model using C5.0 with Gain Ratio for splitting
C5Fit_70 <- C5.0(status ~ ., data = train_data_70, control = C5.0Control(earlyStopping = FALSE, CF = 0.25))

# Print the summary of the model
summary(C5Fit_70)

Call:
C5.0.formula(formula = status ~ ., data = train_data_70, control = C5.0Control(earlyStopping = FALSE, CF = 0.25))


C5.0 [Release 2.07 GPL Edition]     Sat Dec  2 20:49:18 2023
-------------------------------

Class specified by attribute `outcome'

Read 381 cases (40 attributes) from undefined.data

Decision tree:

relationships <= 2:
:...is_top500 <= 0: 0 (44)
:   is_top500 > 0:
:   :...has_roundD > 0: 1 (3/1)
:       has_roundD <= 0:
:       :...category_code <= 18: 0 (19)
:           category_code > 18:
:           :...is_NY > 0: 1 (2)
:               is_NY <= 0:
:               :...is_MA > 0: 0 (6)
:                   is_MA <= 0:
:                   :...funding_rounds > 2: 1 (3)
:                       funding_rounds <= 2:
:                       :...age_last_milestone_year > 5: 1 (3)
:                           age_last_milestone_year <= 5:
:                           :...has_roundB <= 0: 0 (15/2)
:                               has_roundB > 0:
:                               :...avg_participants <= 2: 1 (3)
:                                   avg_participants > 2: 0 (3)
relationships > 2:
:...funding_total_usd <= 0.007181063:
    :...is_otherstate <= 0: 0 (12)
    :   is_otherstate > 0:
    :   :...first_funding_at <= 2009: 0 (7)
    :       first_funding_at > 2009: 1 (2)
    funding_total_usd > 0.007181063:
    :...milestones <= 0:
        :...has_roundC > 0: 1 (4)
        :   has_roundC <= 0:
        :   :...has_roundB <= 0: 0 (19/1)
        :       has_roundB > 0:
        :       :...founded_at <= 2003: 1 (2)
        :           founded_at > 2003: 0 (3/1)
        milestones > 0:
        :...is_MA > 0:
            :...milestones <= 3: 1 (17/1)
            :   milestones > 3: 0 (3/1)
            is_MA <= 0:
            :...has_roundD > 0: 1 (12/2)
                has_roundD <= 0:
                :...is_advertising > 0: 1 (14/3)
                    is_advertising <= 0:
                    :...is_software > 0:
                        :...is_TX <= 0: 1 (26/5)
                        :   is_TX > 0: 0 (3/1)
                        is_software <= 0:
                        :...has_VC > 0:
                            :...has_angel > 0:
                            :   :...is_NY <= 0: 0 (6)
                            :   :   is_NY > 0:
                            :   :   :...age_first_milestone_year <= 1: 1 (2)
                            :   :       age_first_milestone_year > 1: 0 (2)
                            :   has_angel <= 0:
                            :   :...age_first_funding_year > 4: 0 (2)
                            :       age_first_funding_year <= 4:
                            :       :...has_roundC > 0: 1 (3)
                            :           has_roundC <= 0:
                            :           :...name <= 639: 1 (20/5)
                            :               name > 639: 0 (4)
                            has_VC <= 0:
                            :...is_enterprise > 0:
                                :...name > 355: 1 (11)
                                :   name <= 355:
                                :   :...state_code <= 14: 0 (3)
                                :       state_code > 14: 1 (2)
                                is_enterprise <= 0:
                                :...funding_total_usd > 0.5928931: 0 (6/1)
                                    funding_total_usd <= 0.5928931:
                                    :...funding_rounds > 2: [S1]
                                        funding_rounds <= 2:
                                        :...is_NY > 0:
                                            :...milestones > 2: 1 (6)
                                            :   milestones <= 2:
                                            :   :...has_roundA <= 0: 1 (3)
                                            :       has_roundA > 0: 0 (3)
                                            is_NY <= 0:
                                            :...milestones > 3: [S2]
                                                milestones <= 3:
                                                :...is_top500 <= 0:
                                                    :...name <= 240: 0 (5)
                                                    :   name > 240: 1 (8/1)
                                                    is_top500 > 0: [S3]

SubTree [S1]

age_last_milestone_year > 2: 1 (12)
age_last_milestone_year <= 2:
:...is_othercategory <= 0: 1 (2)
    is_othercategory > 0: 0 (2)

SubTree [S2]

funding_total_usd <= 0.1268654: 0 (7)
funding_total_usd > 0.1268654: 1 (3)

SubTree [S3]

age_last_milestone_year > 5: 1 (8)
age_last_milestone_year <= 5:
:...milestones > 2:
    :...city <= 112: 0 (3/1)
    :   city > 112: 1 (10/1)
    milestones <= 2:
    :...avg_participants > 4: 0 (4)
        avg_participants <= 4:
        :...milestones > 1:
            :...age_first_funding_year <= 0: 0 (3/1)
            :   age_first_funding_year > 0: 1 (5)
            milestones <= 1:
            :...age_first_milestone_year <= 2: 1 (3)
                age_first_milestone_year > 2:
                :...funding_total_usd <= 0.3196314: 0 (6/1)
                    funding_total_usd > 0.3196314: 1 (2)


Evaluation on training data (381 cases):

        Decision Tree   
      ----------------  
      Size      Errors  

        53   29( 7.6%)   <<


       (a)   (b)    <-classified as
      ----  ----
       180    19    (a): class 0
        10   172    (b): class 1


    Attribute usage:

    100.00% relationships
     73.49% funding_total_usd
     70.34% has_roundD
     69.29% is_MA
     67.98% milestones
     52.23% is_advertising
     48.56% is_software
     41.47% is_top500
     40.94% has_VC
     32.55% is_NY
     32.02% funding_rounds
     30.71% is_enterprise
     22.05% age_last_milestone_year
     14.44% has_roundC
     14.17% category_code
     13.91% name
     11.81% has_roundB
     10.24% has_angel
      9.71% age_first_funding_year
      7.61% is_TX
      7.61% avg_participants
      5.51% is_otherstate
      3.94% age_first_milestone_year
      3.41% city
      2.36% first_funding_at
      1.57% has_roundA
      1.31% state_code
      1.31% founded_at
      1.05% is_othercategory


Time: 0.0 secs

The code builds the decision tree model.

B.2) Plotting

plot(C5Fit_70, type="simple")

Root Node (Node 1):
  • Splitting attribute: “relationships” <= 2.
  • The algorithm considers the “relationships” feature for the first decision.

Branches:
  • Node 2: If “relationships” <= 2, it evaluates the next criterion.
  • Node 7: If “relationships” > 2, it evaluates a different criterion.

Leaf Nodes (Terminal Nodes):
  • Node 4, Node 5, Node 6, Node 9, Node 10, Node 11: Terminal nodes where the decision tree makes predictions.

Number of Nodes: 53 (1 root, 26 internal, 26 leaf)

Most Important Features:
  1. relationships: Critical as the root node, indicating high importance (100.00%).
  2. funding_total_usd: Significant for further splits, showcasing its relevance (73.49%).
  3. has_roundD: Plays a key role in decision-making (70.34%).

In summary, this tree utilizes the “relationships” feature for the initial split, followed by additional criteria at each branching point, ultimately leading to predictions at the terminal nodes. The weights associated with each terminal node indicate the number of observations falling into each category. The most important features, as identified by the attribute usage, include “relationships,” “funding_total_usd,” and “has_roundD.”

B.3) Testing

predictions_GR_70 <- predict(C5Fit_70, newdata = test_data_70)
labels_GR_70 <- test_data_70$status

The code generates predictions for the test dataset using the decision tree model.

B.4) Confusion Matrix

# Create a confusion matrix
confusion_matrix_GR_70 <- table(test_data_70$status, predictions_GR_70)

# Display the confusion matrix
print(confusion_matrix_GR_70)
   predictions_GR_70
     0  1
  0 48 28
  1 27 66

The confusion matrix for the provided code indicates the following:

•   True Positive (TP): 66 cases were correctly predicted as 1.<br>
•   False Positive (FP): 28 cases were incorrectly predicted as 1, and 27 cases were incorrectly predicted as 0.<br>
•   True Negative (TN): 48 cases were correctly predicted as 0.<br>
•   False Negative (FN): 27 cases were incorrectly predicted as 0.<br>

B.5) Evaluation Metrics

metrics_GR_70 <- evaluate_model(predictions_GR_70, labels_GR_70)
             predictions
actual_labels  0  1
            0 48 28
            1 27 66
Accuracy: 67.45562 %
Precision: 64 %
Sensitivity (Recall): 63.15789 %
Specificity: 70.96774 %

• Accuracy (67.46%): This metric represents the overall correctness of the model’s predictions. In this case, the model achieved an accuracy of 67.46%, indicating a relatively moderate level of correctness.

• Precision (64%): Precision measures the accuracy of positive predictions. In this context, the model achieved a precision of 64%, indicating that when it predicted a positive outcome, it was correct in 64% of cases.

• Sensitivity (Recall) (63.16%): Sensitivity, also known as recall, measures the ability of the model to correctly identify positive instances. In this case, the model’s sensitivity is 63.16%, suggesting a moderate performance in capturing positive instances.

• Specificity (70.97%): Specificity measures the ability of the model to correctly identify negative instances. With a specificity of 70.97%, the model performed relatively well in accurately identifying negative cases.

In summary, while the model exhibited moderate accuracy and sensitivity, the precision and specificity suggest a reasonable ability to make correct predictions, especially in identifying negative instances.

CART Algorithm: Gini Index

C.1) Constructing the Decision Tree

library(rpart)
# Gini index (CART) and Tree model 70:30
preprocessed_dataset_ctree_GI_70 <- rpart(status ~ ., data = train_data_70, method = "class", parms = list(split = "gini"))

The code builds the decision tree model.

C.2) Plotting

library(rpart.plot)
print(preprocessed_dataset_ctree_GI_70)
n= 381 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

 1) root 381 182 0 (0.5223097 0.4776903)  
   2) relationships< 2.5 101  15 0 (0.8514851 0.1485149) *
   3) relationships>=2.5 280 113 1 (0.4035714 0.5964286)  
     6) funding_total_usd< 0.00723402 21   2 0 (0.9047619 0.0952381) *
     7) funding_total_usd>=0.00723402 259  94 1 (0.3629344 0.6370656)  
      14) milestones< 0.5 28   8 0 (0.7142857 0.2857143)  
        28) funding_total_usd< 0.1565217 14   0 0 (1.0000000 0.0000000) *
        29) funding_total_usd>=0.1565217 14   6 1 (0.4285714 0.5714286) *
      15) milestones>=0.5 231  74 1 (0.3203463 0.6796537) *
rpart.plot(preprocessed_dataset_ctree_GI_70)

Root Node (Node 1):
  • Splitting attribute: “relationships” < 2.5, considering 381 observations with an expected loss of 0.4776903 (52.2% class 0, 47.8% class 1).

Branches:
  • Node 2: If “relationships” < 2.5, it predicts class 0 with an expected loss of 0.1485149 (85.1% class 0, 14.9% class 1).
  • Node 3: If “relationships” >= 2.5, it predicts class 1 with an expected loss of 0.4035714 (40.4% class 0, 59.6% class 1).
  • Node 6: If “funding_total_usd” < 0.00723402 (under Node 3), it predicts class 0 with an expected loss of 0.0952381 (90.5% class 0, 9.5% class 1).
  • Node 7: If “funding_total_usd” >= 0.00723402 (under Node 3), it predicts class 1 with an expected loss of 0.3629344 (36.3% class 0, 63.7% class 1).
  • Node 14: If “milestones” < 0.5 (under Node 7), it predicts class 0 with an expected loss of 0.2857143 (71.4% class 0, 28.6% class 1).
  • Node 15: If “milestones” >= 0.5 (under Node 7), it predicts class 1 with an expected loss of 0.3203463 (32.0% class 0, 68.0% class 1).

Leaf Nodes (Terminal Nodes):
  • Node 28: Terminal node predicting class 0 (100% class 0, 0% class 1).
  • Node 29: Terminal node predicting class 1 (42.9% class 0, 57.1% class 1).

Number of Nodes: 8 (1 root, 6 internal, 2 leaf)

Most Important Features:
  1. relationships: Critical as the root node, indicating high importance.
  2. funding_total_usd: Significant for further splits, showcasing its relevance.
  3. milestones: Used for additional splits, emphasizing its contribution.

In summary, this tree utilizes the “relationships” feature for the initial split, followed by additional criteria at each branching point, ultimately leading to predictions at the terminal nodes. The weights associated with each terminal node indicate the number of observations falling into each category. ### C.3) Testing

predictions_GI_70 <- predict(preprocessed_dataset_ctree_GI_70, newdata = test_data_70, type = "class")
labels_GI_70 <- test_data_70$status

The code generates predictions for the test dataset using the decision tree model.

C.4) Confusion Matrix

# Create a confusion matrix
confusion_matrix_GI_70 <- table(test_data_70$status, predictions_GI_70)

# Display the confusion matrix
print(confusion_matrix_GI_70)
   predictions_GI_70
     0  1
  0 50 26
  1  9 84

The confusion matrix for the provided code indicates the following:

•   True Positive (TP): 84 cases were correctly predicted as 1.<br>
•   False Positive (FP): 26 cases were incorrectly predicted as 1, and 9 cases were incorrectly predicted as 0.<br>
•   True Negative (TN): 50 cases were correctly predicted as 0.<br>
•   False Negative (FN): 9 cases were incorrectly predicted as 0.<br>

C.5) Evaluation Metrics

metrics_GI_70 <- evaluate_model(predictions_GI_70, labels_GI_70)
             predictions
actual_labels  0  1
            0 50 26
            1  9 84
Accuracy: 79.28994 %
Precision: 84.74576 %
Sensitivity (Recall): 65.78947 %
Specificity: 90.32258 %

• Accuracy (79.29%): This metric represents the overall correctness of the model’s predictions. In this case, the model achieved an accuracy of 79.29%, indicating a relatively high level of correctness.

• Precision (84.75%): Precision measures the accuracy of positive predictions. In this context, the model achieved a precision of 84.75%, indicating that when it predicted a positive outcome, it was correct in 84.75% of cases.

• Sensitivity (Recall) (65.79%): Sensitivity, also known as recall, measures the ability of the model to correctly identify positive instances. In this case, the model’s sensitivity is 65.79%, suggesting a moderate performance in capturing positive instances.

• Specificity (90.32%): Specificity measures the ability of the model to correctly identify negative instances. With a specificity of 90.32%, the model performed well in accurately identifying negative cases.

In summary, the model exhibited high accuracy and precision, with good specificity. However, the sensitivity indicates that there might be room for improvement in capturing positive instances.


Second Partition: (80% training, 20% testing)

Partitioning

set.seed(1234)
ind=sample(2, nrow(balanced_preprocessed_dataset), replace=TRUE, prob=c(0.80 , 0.20))
train_data_80=balanced_preprocessed_dataset[ind==1,]
test_data_80=balanced_preprocessed_dataset[ind==2,]

The code splits the preprocessed dataset into 80% training data and 20% testing data.

Verify Partitioning

dim(train_data_80)
[1] 443  40
dim(test_data_80)
[1] 107  40

The training data consist of 443 rows. The testing data consist of 107 rows.

A.1) Constructing the Decision Tree

library(party)
myFormula <- status ~ state_code + city + name + founded_at + closed_at + first_funding_at + last_funding_at + 
            age_first_funding_year + age_last_funding_year + age_first_milestone_year + age_last_milestone_year + 
            relationships + funding_rounds + funding_total_usd + milestones + is_CA + is_NY + is_MA + is_TX + is_otherstate + 
            category_code + is_software + is_web + is_mobile + is_enterprise + is_advertising + is_gamesvideo + is_ecommerce + 
            is_biotech + is_consulting + is_othercategory + has_VC + has_angel + has_roundA + has_roundB + has_roundC + 
            has_roundD + avg_participants + is_top500
preprocessed_dataset_ctree_IG_80 <- ctree(myFormula, data = train_data_80)

table(predict(preprocessed_dataset_ctree_IG_80), train_data_80$status)
   
      0   1
  0 127  29
  1 100 187

The code builds the decision tree model.

A.2) Plotting

print(preprocessed_dataset_ctree_IG_80)

     Conditional inference tree with 5 terminal nodes

Response:  status 
Inputs:  state_code, city, name, founded_at, closed_at, first_funding_at, last_funding_at, age_first_funding_year, age_last_funding_year, age_first_milestone_year, age_last_milestone_year, relationships, funding_rounds, funding_total_usd, milestones, is_CA, is_NY, is_MA, is_TX, is_otherstate, category_code, is_software, is_web, is_mobile, is_enterprise, is_advertising, is_gamesvideo, is_ecommerce, is_biotech, is_consulting, is_othercategory, has_VC, has_angel, has_roundA, has_roundB, has_roundC, has_roundD, avg_participants, is_top500 
Number of observations:  443 

1) relationships <= 3; criterion = 1, statistic = 70.905
  2) milestones <= 2; criterion = 0.999, statistic = 18.195
    3) funding_total_usd <= 0.2285442; criterion = 0.99, statistic = 13.446
      4)*  weights = 109 
    3) funding_total_usd > 0.2285442
      5)*  weights = 47 
  2) milestones > 2
    6)*  weights = 14 
1) relationships > 3
  7) age_last_milestone_year <= 4; criterion = 0.993, statistic = 13.937
    8)*  weights = 182 
  7) age_last_milestone_year > 4
    9)*  weights = 91 
plot(preprocessed_dataset_ctree_IG_80)

plot(preprocessed_dataset_ctree_IG_80, type = "simple")

Root Node (Node 1):
  • Splitting attribute: “relationships” < 3, considering 443 observations with a criterion of 1 and a statistic of 70.905 (expected loss: 70.905%).

Branches:
  Node 2: If “relationships” <= 3, it follows the criterion with a statistic of 18.195.
  • Node 3: If “relationships” > 3, it follows a different criterion with a statistic of 13.937.

Leaf Nodes (Terminal Nodes):
  • Node 4: If “milestones” <= 2 and “funding_total_usd” <= 0.2285442, it predicts status 0 with weights = 109.
  • Node 5: If “milestones” <= 2 and “funding_total_usd” > 0.2285442, it predicts status 1 with weights = 47.
  • Node 6: If “milestones” > 2, it predicts status 1 with weights = 14.
  • Node 7: If “age_last_milestone_year” <= 4, it predicts status 0 with weights = 182.
  • Node 8: If “age_last_milestone_year” > 4, it predicts status 1 with weights = 91.

Number of Nodes: 9 (1 root, 4 internal, 5 leaf)

Most Important Features:
  1. relationships: Critical as the root node, indicating high importance (100.00%).
  2. milestones: Significant for splits, emphasizing its relevance (96.61%).
  3. funding_total_usd: Plays a role in decisions, showcasing its importance (7.67%).

In summary, this decision tree, rooted in the “relationships” attribute, efficiently classifies observations based on key features. With 5 terminal nodes, it provides distinct predictions for different paths in the decision-making process. The primary attributes influencing decisions include “relationships,” “milestones,” and “funding_total_usd.” The tree’s structure emphasizes the significance of these features in determining the final status prediction for startups.

A.3) Testing

predictions_IG_80 <- predict(preprocessed_dataset_ctree_IG_80, newdata = test_data_80, type = "response")
labels_IG_80 <- test_data_80$status

The code generates predictions for the test dataset using the decision tree model.

A.4) Confusion Matrix

confusion_matrix_IG_80 <- table(test_data_80$status, predictions_IG_80)
print(confusion_matrix_IG_80)
   predictions_IG_80
    0.091743119266055 0.404255319148936 0.582417582417582 0.642857142857143 0.791208791208791
  0                23                 8                12                 1                 4
  1                 4                 5                31                 2                17

The confusion matrix for the provided code indicates the following:

•   True Positive (TP): 31 cases were correctly predicted as 0.5824.<br>
•   False Positive (FP): 12 cases were incorrectly predicted as 0.4043, 1 case as 0.5824, 4 cases as 0.09174, and 2 cases as 0.6429.<br>
•   True Negative (TN): 23 cases were correctly predicted as 0.09174, 8 cases as 0.4043, and 17 cases as 0.6429.<br>
•   False Negative (FN): 4 cases were incorrectly predicted as 0.09174 and 5 cases as 0.4043.<br>

A.5) Evaluation Metrics

metrics_IG_80 <- evaluate_model(predictions_IG_80, labels_IG_80)
             predictions
actual_labels 0.091743119266055 0.404255319148936 0.582417582417582 0.642857142857143 0.791208791208791
            0                23                 8                12                 1                 4
            1                 4                 5                31                 2                17
Accuracy: 26.16822 %
Precision: 85.18519 %
Sensitivity (Recall): 74.19355 %
Specificity: 55.55556 %
print(metrics_IG_80)
$accuracy
[1] 26.16822

$precision
[1] 85.18519

$sensitivity
[1] 74.19355

$specificity
[1] 55.55556

• Accuracy (26.17%): This metric represents the overall correctness of the model’s predictions. In this case, the model achieved an accuracy of 26.17%, indicating a relatively low level of correctness.

• Precision (85.19%): Precision measures the accuracy of positive predictions. In this context, the model achieved a precision of 85.19%, indicating that when it predicted a positive outcome, it was correct in 85.19% of cases.

• Sensitivity (Recall) (74.19%): Sensitivity, also known as recall, measures the ability of the model to correctly identify positive instances. In this case, the model’s sensitivity is 74.19%, suggesting a moderate performance in capturing positive instances.

• Specificity (55.56%): Specificity measures the ability of the model to correctly identify negative instances. With a specificity of 55.56%, the model performed moderately well in accurately identifying negative cases.

In summary, the model exhibited low accuracy but high precision. The sensitivity indicates a reasonable ability to capture positive instances, while the specificity suggests room for improvement in identifying negative cases.

B) C50 Algorithm: Gain Ratio

B.1) Constructing the Decision Tree

library(C50)
train_data_80$status <- as.factor(train_data_80$status)

C5Fit_80 <- C5.0(status ~ ., data = train_data_80, control = C5.0Control(earlyStopping = FALSE, CF = 0.25))
summary(C5Fit_80)

Call:
C5.0.formula(formula = status ~ ., data = train_data_80, control = C5.0Control(earlyStopping = FALSE, CF = 0.25))


C5.0 [Release 2.07 GPL Edition]     Sat Dec  2 20:49:20 2023
-------------------------------

Class specified by attribute `outcome'

Read 443 cases (40 attributes) from undefined.data

Decision tree:

relationships <= 3:
:...age_last_milestone_year > 5:
:   :...is_web > 0: 0 (2)
:   :   is_web <= 0:
:   :   :...has_VC <= 0: 1 (8)
:   :       has_VC > 0:
:   :       :...age_last_milestone_year <= 6: 1 (2)
:   :           age_last_milestone_year > 6: 0 (3)
:   age_last_milestone_year <= 5:
:   :...milestones > 2:
:       :...is_web <= 0: 1 (6)
:       :   is_web > 0: 0 (5/1)
:       milestones <= 2:
:       :...is_TX > 0:
:           :...relationships <= 1: 0 (7/1)
:           :   relationships > 1: 1 (4)
:           is_TX <= 0:
:           :...funding_rounds <= 2:
:               :...has_roundA <= 0: 0 (84/5)
:               :   has_roundA > 0:
:               :   :...funding_total_usd <= 0.3598793: 0 (29/1)
:               :       funding_total_usd > 0.3598793: 1 (5/1)
:               funding_rounds > 2:
:               :...is_web > 0: 0 (2)
:                   is_web <= 0:
:                   :...category_code <= 20: 0 (9/2)
:                       category_code > 20: 1 (4)
relationships > 3:
:...milestones <= 0:
    :...has_roundC > 0: 1 (4)
    :   has_roundC <= 0:
    :   :...has_roundB <= 0: 0 (16/1)
    :       has_roundB > 0: 1 (6/2)
    milestones > 0:
    :...founded_at <= 2003: 1 (32/1)
        founded_at > 2003:
        :...is_top500 <= 0:
            :...last_funding_at > 2011: 1 (12/2)
            :   last_funding_at <= 2011:
            :   :...is_TX > 0: 0 (2)
            :       is_TX <= 0:
            :       :...has_roundB > 0: 0 (2)
            :           has_roundB <= 0:
            :           :...age_last_funding_year <= 0: 0 (11/1)
            :               age_last_funding_year > 0:
            :               :...age_first_milestone_year > 3: 1 (3)
            :                   age_first_milestone_year <= 3:
            :                   :...age_last_funding_year > 2: 0 (4)
            :                       age_last_funding_year <= 2:
            :                       :...is_CA <= 0: 1 (4)
            :                           is_CA > 0:
            :                           :...name <= 602: 0 (4)
            :                               name > 602: 1 (4)
            is_top500 > 0:
            :...founded_at > 2009:
                :...has_VC > 0: 0 (5)
                :   has_VC <= 0:
                :   :...is_otherstate > 0: 1 (3)
                :       is_otherstate <= 0:
                :       :...milestones > 2: 1 (5/1)
                :           milestones <= 2:
                :           :...first_funding_at <= 2010: 0 (8/1)
                :               first_funding_at > 2010:
                :               :...avg_participants <= 4: 1 (3)
                :                   avg_participants > 4: 0 (3)
                founded_at <= 2009:
                :...avg_participants > 5: 1 (11)
                    avg_participants <= 5:
                    :...relationships > 8: 1 (60/8)
                        relationships <= 8:
                        :...is_MA > 0: 1 (6)
                            is_MA <= 0:
                            :...has_roundC > 0:
                                :...city <= 93: 1 (2)
                                :   city > 93: 0 (5)
                                has_roundC <= 0:
                                :...age_last_funding_year > 3: 0 (10/2)
                                    age_last_funding_year <= 3:
                                    :...has_roundA <= 0: 1 (11/1)
                                        has_roundA > 0:
                                        :...is_NY > 0: 0 (3)
                                            is_NY <= 0:
                                            :...is_mobile > 0: 0 (5/1)
                                                is_mobile <= 0:
                                                :...is_web > 0: 1 (4)
                                                    is_web <= 0:
                                                    :...name <= 72: 0 (3)
                                                        name > 72: 1 (22/5)


Evaluation on training data (443 cases):

        Decision Tree   
      ----------------  
      Size      Errors  

        45   37( 8.4%)   <<


       (a)   (b)    <-classified as
      ----  ----
       206    21    (a): class 0
        16   200    (b): class 1


    Attribute usage:

    100.00% relationships
     96.61% milestones
     55.76% founded_at
     48.53% is_top500
     40.18% is_TX
     38.37% age_last_milestone_year
     37.47% has_roundA
     33.41% avg_participants
     30.02% funding_rounds
     20.54% has_roundC
     19.86% age_last_funding_year
     16.03% is_MA
     15.80% is_web
     12.19% has_roundB
     10.38% last_funding_at
      9.03% has_VC
      8.35% is_NY
      7.67% funding_total_usd
      7.67% is_mobile
      7.45% name
      4.97% is_otherstate
      4.29% age_first_milestone_year
      3.16% first_funding_at
      2.93% category_code
      2.71% is_CA
      1.58% city


Time: 0.0 secs

The code builds the decision tree model.

B.2) Plotting

plot(C5Fit_80, type = "simple")

Root Node (Node 1):
  • Splitting attribute: “relationships” <= 3, considering 443 observations with a criterion of 1 and a statistic of 70.905 (expected loss: 70.905%).

Branches:
  • Node 2: If “relationships” <= 3, it evaluates the next criterion with a statistic of 18.195.
  • Node 3: If “relationships” > 3, it evaluates a different criterion with a statistic of 13.937.

Leaf Nodes (Terminal Nodes):
  • Node 4: Terminal node where the decision tree predicts based on weights = 109.
  • Node 5: Terminal node where the decision tree predicts based on weights = 47.
  • Node 6: Terminal node where the decision tree predicts based on weights = 14.
  • Node 7: Terminal node where the decision tree predicts based on weights = 182.
  • Node 8: Terminal node where the decision tree predicts based on weights = 91.

Number of Nodes: 9 (1 root, 4 internal, 5 leaf)

Most Important Features:
  1. relationships: Critical as the root node, indicating high importance (100.00%).
  2. milestones: Significant for further splits, showcasing its relevance (96.61%).
  3. founded_at: Plays a key role in decision-making (55.76%).

In summary, this decision tree, rooted in the “relationships” attribute, efficiently classifies observations based on key features. With 5 terminal nodes, it provides distinct predictions for different paths in the decision-making process. The primary attributes influencing decisions include “relationships,” “milestones,” and “founded_at.” The tree’s structure emphasizes the significance of these features in determining the final status prediction for startups.

B.3) Testing

predictions_GR_80 <- predict(C5Fit_80, newdata = test_data_80)
labels_GR_80 <- test_data_80$status

The code generates predictions for the test dataset using the decision tree model.

B.4) Confusion Matrix

confusion_matrix_GR_80 <- table(test_data_80$status, predictions_GR_80)
print(confusion_matrix_GR_80)
   predictions_GR_80
     0  1
  0 36 12
  1 13 46

The confusion matrix for the provided code indicates the following:

•   True Positive (TP): 46 cases were correctly predicted as 1.<br>
•   False Positive (FP): 12 cases were incorrectly predicted as 1, and 13 cases were incorrectly predicted as 0.<br>
•   True Negative (TN): 36 cases were correctly predicted as 0.<br>
•   False Negative (FN): 0 cases were incorrectly predicted as 0.<br>

B.5) Evaluation Metrics

metrics_GR_80 <- evaluate_model(predictions_GR_80, labels_GR_80)
             predictions
actual_labels  0  1
            0 36 12
            1 13 46
Accuracy: 76.63551 %
Precision: 73.46939 %
Sensitivity (Recall): 75 %
Specificity: 77.9661 %

• Accuracy (76.64%): This metric represents the overall correctness of the model’s predictions. In this case, the model achieved an accuracy of 76.64%, indicating a relatively high level of correctness.

• Precision (73.47%): Precision measures the accuracy of positive predictions. In this context, the model achieved a precision of 73.47%, indicating that when it predicted a positive outcome, it was correct in 73.47% of cases.

• Sensitivity (Recall) (75%): Sensitivity, also known as recall, measures the ability of the model to correctly identify positive instances. In this case, the model’s sensitivity is 75%, suggesting a good performance in capturing positive instances.

• Specificity (77.97%): Specificity measures the ability of the model to correctly identify negative instances. With a specificity of 77.97%, the model performed well in accurately identifying negative cases.

In summary, the model exhibited high accuracy, precision, and sensitivity. The specificity indicates a good ability to identify negative instances.

CART Algorithm: Gini Index

C.1) Constructing the Decision Tree

library(rpart)
preprocessed_dataset_ctree_GI_80 <- rpart(status ~ ., data = train_data_80, method = "class", parms = list(split = "gini"))

The code builds the decision tree model.

C.2) Plotting

print(preprocessed_dataset_ctree_GI_80)
n= 443 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

 1) root 443 216 0 (0.5124153 0.4875847)  
   2) relationships< 3.5 170  38 0 (0.7764706 0.2235294)  
     4) age_last_milestone_year< 5.5 155  28 0 (0.8193548 0.1806452)  
       8) milestones< 2.5 144  21 0 (0.8541667 0.1458333) *
       9) milestones>=2.5 11   4 1 (0.3636364 0.6363636) *
     5) age_last_milestone_year>=5.5 15   5 1 (0.3333333 0.6666667) *
   3) relationships>=3.5 273  95 1 (0.3479853 0.6520147)  
     6) age_last_funding_year< 0.5 46  18 0 (0.6086957 0.3913043)  
      12) milestones< 2.5 28   4 0 (0.8571429 0.1428571) *
      13) milestones>=2.5 18   4 1 (0.2222222 0.7777778) *
     7) age_last_funding_year>=0.5 227  67 1 (0.2951542 0.7048458)  
      14) funding_total_usd< 0.1168035 84  34 1 (0.4047619 0.5952381)  
        28) funding_rounds< 2.5 63  30 1 (0.4761905 0.5238095)  
          56) funding_total_usd>=0.07421676 22   7 0 (0.6818182 0.3181818) *
          57) funding_total_usd< 0.07421676 41  15 1 (0.3658537 0.6341463) *
        29) funding_rounds>=2.5 21   4 1 (0.1904762 0.8095238) *
      15) funding_total_usd>=0.1168035 143  33 1 (0.2307692 0.7692308) *
rpart.plot(preprocessed_dataset_ctree_GI_80)

Root Node (Node 1):
  • Splitting attribute: “relationships” < 3.5, considering 443 observations with an expected loss of 0.5124153 (51.2% class 0, 48.8% class 1).

Branches:
  • Node 2: If “relationships” < 3.5, it predicts class 0 with an expected loss of 0.7764706 (77.6% class 0, 22.4% class 1).
  • Node 4: If “age_last_milestone_year” < 5.5 (under Node 2), it predicts class 0 with an expected loss of 0.8541667 (85.4% class 0, 14.6% class 1).
  • Node 5: If “age_last_milestone_year” >= 5.5 (under Node 2), it predicts class 1 with an expected loss of 0.3333333 (33.3% class 0, 66.7% class 1).

  • Node 3: If “relationships” >= 3.5, it predicts class 1 with an expected loss of 0.3479853 (34.8% class 0, 65.2% class 1).
  • Node 6: If “age_last_funding_year” < 0.5 (under Node 3), it predicts class 0 with an expected loss of 0.6086957 (60.9% class 0, 39.1% class 1).
  • Node 12: If “milestones” < 2.5 (under Node 6), it predicts class 0 with an expected loss of 0.8571429 (85.7% class 0, 14.3% class 1).
  • Node 13: If “milestones” >= 2.5 (under Node 6), it predicts class 1 with an expected loss of 0.2222222 (22.2% class 0, 77.8% class 1).

  • Node 7: If “age_last_funding_year” >= 0.5 (under Node 3), it predicts class 1 with an expected loss of 0.2951542 (29.5% class 0, 70.5% class 1).
  • Node 14: If “funding_total_usd” < 0.1168035 (under Node 7), it predicts class 1 with an expected loss of 0.4047619 (40.5% class 0, 59.5% class 1).
  • Node 28: Terminal node predicting class 0 (100% class 0, 0% class 1).
  • Node 29: Terminal node predicting class 1 with an expected loss of 0.4285714 (42.9% class 0, 57.1% class 1).

Number of Nodes: 8 (1 root, 6 internal, 2 leaf)

Most Important Features:
  1. relationships: Critical as the root node, indicating high importance.
  2. age_last_milestone_year: Significant for further splits, showcasing its relevance.
  3. age_last_funding_year: Used for additional splits, emphasizing its contribution.

In summary, this tree utilizes the “relationships” feature for the initial split, followed by additional criteria at each branching point, ultimately leading to predictions at the terminal nodes. The weights associated with each terminal node indicate the number of observations falling into each category. For this tree, n= 443.

C.3) Testing

predictions_GI_80 <- predict(preprocessed_dataset_ctree_GI_80, newdata = test_data_80, type = "class")
labels_GI_80 <- test_data_80$status

The code generates predictions for the test dataset using the decision tree model.

C.4) Confusion Matrix

confusion_matrix_GI_80 <- table(test_data_80$status, predictions_GI_80)
print(confusion_matrix_GI_80)
   predictions_GI_80
     0  1
  0 33 15
  1 13 46

The confusion matrix for the provided code indicates the following:

•   True Positive (TP): 46 cases were correctly predicted as 1.<br>
•   False Positive (FP): 15 cases were incorrectly predicted as 1, and 13 cases were incorrectly predicted as 0.<br>
•   True Negative (TN): 33 cases were correctly predicted as 0.<br>
•   False Negative (FN): 0 cases were incorrectly predicted as 0.<br>

C.5) Evaluation Metrics

metrics_GI_80 <- evaluate_model(predictions_GI_80, labels_GI_80)
             predictions
actual_labels  0  1
            0 33 15
            1 13 46
Accuracy: 73.83178 %
Precision: 71.73913 %
Sensitivity (Recall): 68.75 %
Specificity: 77.9661 %

• Accuracy (73.83%): This metric represents the overall correctness of the model’s predictions. In this case, the model achieved an accuracy of 73.83%, indicating a relatively high level of correctness.

• Precision (71.74%): Precision measures the accuracy of positive predictions. In this context, the model achieved a precision of 71.74%, indicating that when it predicted a positive outcome, it was correct in 71.74% of cases.

• Sensitivity (Recall) (68.75%): Sensitivity, also known as recall, measures the ability of the model to correctly identify positive instances. In this case, the model’s sensitivity is 68.75%, suggesting a moderate performance in capturing positive instances.

• Specificity (77.97%): Specificity measures the ability of the model to correctly identify negative instances. With a specificity of 77.97%, the model performed well in accurately identifying negative cases.

In summary, the model exhibited high accuracy, and specificity. The precision and sensitivity suggest a reasonable ability to make correct predictions, especially in capturing positive instances.


Third Partition: (90% training, 10% testing)

Partitioning

set.seed(1234)
ind <- sample(2, nrow(balanced_preprocessed_dataset), replace=TRUE, prob=c(0.90, 0.10))
train_data_90 <- balanced_preprocessed_dataset[ind == 1,]
test_data_90 <- balanced_preprocessed_dataset[ind == 2,]

The code splits the preprocessed dataset into 90% training data and 10% testing data.

Verify Partitioning

dim(train_data_90)
[1] 487  40
dim(test_data_90)
[1] 63 40

The training data consist of 487 rows. The testing data consist of 63 rows.

A.1) Constructing the Decision Tree

library(party)
myFormula <- status ~ state_code + city + name + founded_at + closed_at + first_funding_at + last_funding_at + 
            age_first_funding_year + age_last_funding_year + age_first_milestone_year + age_last_milestone_year + 
            relationships + funding_rounds + funding_total_usd + milestones + is_CA + is_NY + is_MA + is_TX + is_otherstate + 
            category_code + is_software + is_web + is_mobile + is_enterprise + is_advertising + is_gamesvideo + is_ecommerce + 
            is_biotech + is_consulting + is_othercategory + has_VC + has_angel + has_roundA + has_roundB + has_roundC + 
            has_roundD + avg_participants + is_top500
preprocessed_dataset_ctree_IG_90 <- ctree(myFormula, data = train_data_90)

table(predict(preprocessed_dataset_ctree_IG_90), train_data_90$status)
                    
                       0   1
  0.0704225352112676  66   5
  0.181818181818182    9   2
  0.264705882352941   75  27
  0.46                27  23
  0.642857142857143    5   9
  0.719665271966527   67 172

The code builds the decision tree model.

A.2) Plotting

print(preprocessed_dataset_ctree_IG_90)

     Conditional inference tree with 6 terminal nodes

Response:  status 
Inputs:  state_code, city, name, founded_at, closed_at, first_funding_at, last_funding_at, age_first_funding_year, age_last_funding_year, age_first_milestone_year, age_last_milestone_year, relationships, funding_rounds, funding_total_usd, milestones, is_CA, is_NY, is_MA, is_TX, is_otherstate, category_code, is_software, is_web, is_mobile, is_enterprise, is_advertising, is_gamesvideo, is_ecommerce, is_biotech, is_consulting, is_othercategory, has_VC, has_angel, has_roundA, has_roundB, has_roundC, has_roundD, avg_participants, is_top500 
Number of observations:  487 

1) relationships <= 3; criterion = 1, statistic = 85.513
  2) milestones <= 2; criterion = 0.999, statistic = 18.362
    3) is_top500 <= 0; criterion = 0.953, statistic = 10.421
      4)*  weights = 71 
    3) is_top500 > 0
      5)*  weights = 102 
  2) milestones > 2
    6)*  weights = 14 
1) relationships > 3
  7) age_last_milestone_year <= 0; criterion = 0.995, statistic = 14.789
    8)*  weights = 11 
  7) age_last_milestone_year > 0
    9) is_top500 <= 0; criterion = 0.986, statistic = 12.66
      10)*  weights = 50 
    9) is_top500 > 0
      11)*  weights = 239 
plot(preprocessed_dataset_ctree_IG_90)

plot(preprocessed_dataset_ctree_IG_90, type = "simple")

Root Node (Node 1):
  • Splitting attribute: “relationships” <= 3; criterion = 1, statistic = 85.513, considering 487 observations.

Branches:
  • Node 2: If “relationships” <= 3, it proceeds to the next split based on “milestones” <= 2; criterion = 0.999, statistic = 18.362.
  • Node 3: If “milestones” <= 2 and “is_top500” <= 0, it predicts class 0.
  • Node 5: If “milestones” <= 2 and “is_top500” > 0, it predicts class 1.

  • Node 6: If “milestones” > 2, it predicts class 1.
  • Node 7: If “relationships” > 3 and “age_last_milestone_year” <= 0, it predicts class 0.

  • Node 8: If “relationships” > 3 and “age_last_milestone_year” > 0 and “is_top500” <= 0, it predicts class 0. *

Leaf Nodes (Terminal Nodes):
  • Node 4: Terminal node predicting class 0, with weights = 71.
  • Node 5: Terminal node predicting class 1, with weights = 102.
  • Node 6: Terminal node predicting class 1, with weights = 14.
  • Node 8: Terminal node predicting class 0, with weights = 11.

Number of Nodes: 9 (1 root, 5 internal, 4 leaf)

Most Important Features:
  1. relationships: Critical as the root node, indicating high importance.
  2. milestones: Significant for further splits, showcasing its relevance.
  3. is_top500: Used for additional splits, emphasizing its contribution.

In summary, this conditional inference tree utilizes “relationships” as the initial split, followed by criteria based on “milestones” and “is_top500” at different branching points. The terminal nodes provide predictions with associated weights reflecting the number of observations. For this tree, n= 487.

A.3) Testing

predictions_IG_90 <- predict(preprocessed_dataset_ctree_IG_90, newdata = test_data_90, type = "response")
labels_IG_90 <- test_data_90$status

The code generates predictions for the test dataset using the decision tree model.

A.4) Confusion Matrix

confusion_matrix_IG_90 <- table(test_data_90$status, predictions_IG_90)
print(confusion_matrix_IG_90)
   predictions_IG_90
    0.0704225352112676 0.181818181818182 0.264705882352941 0.46 0.642857142857143 0.719665271966527
  0                  6                 2                11    1                 1                 5
  1                  1                 0                 5    1                 2                28

The confusion matrix for the provided code indicates the following:

•   True Positive (TP): 28 cases were correctly predicted as 0.7197.<br>
•   False Positive (FP): 11 cases were incorrectly predicted as 0.2647, 1 case as 0.46, 1 case as 0.6429, 2 cases as 0.07042, and 2 cases as 0.1818.<br>
•   True Negative (TN): 6 cases were correctly predicted as 0.07042, 2 cases as 0.1818, and 5 cases as 0.6429.<br>
•   False Negative (FN): 1 case was incorrectly predicted as 0.07042 and 5 cases as 0.1818.<br>

A.5) Evaluation Metrics

metrics_IG_90 <- evaluate_model(predictions_IG_90, labels_IG_90)
             predictions
actual_labels 0.0704225352112676 0.181818181818182 0.264705882352941 0.46 0.642857142857143 0.719665271966527
            0                  6                 2                11    1                 1                 5
            1                  1                 0                 5    1                 2                28
Accuracy: 9.52381 %
Precision: 85.71429 %
Sensitivity (Recall): 75 %
Specificity: 0 %
print(metrics_IG_90)
$accuracy
[1] 9.52381

$precision
[1] 85.71429

$sensitivity
[1] 75

$specificity
[1] 0

• Accuracy (9.52%): This metric represents the overall correctness of the model’s predictions. In this case, the model achieved an accuracy of 9.52%, indicating a low level of correctness.

• Precision (85.71%): Precision measures the accuracy of positive predictions. In this context, the model achieved a precision of 85.71%, indicating that when it predicted a positive outcome, it was correct in 85.71% of cases.

• Sensitivity (Recall) (75%): Sensitivity, also known as recall, measures the ability of the model to correctly identify positive instances. In this case, the model’s sensitivity is 75%, suggesting a moderate performance in capturing positive instances.

• Specificity (0%): Specificity measures the ability of the model to correctly identify negative instances. With a specificity of 0%, the model did not perform well in accurately identifying negative cases.

In summary, the model exhibited low accuracy and specificity. The high precision and sensitivity suggest a reasonable ability to make correct predictions, especially in capturing positive instances.

B) C50 Algorithm: Gain Ratio

B.1) Constructing the Decision Tree

library(C50)
train_data_90$status <- as.factor(train_data_90$status)

C5Fit_90 <- C5.0(status ~ ., data = train_data_90, control = C5.0Control(earlyStopping = FALSE, CF = 0.25))
summary(C5Fit_90)

Call:
C5.0.formula(formula = status ~ ., data = train_data_90, control = C5.0Control(earlyStopping = FALSE, CF = 0.25))


C5.0 [Release 2.07 GPL Edition]     Sat Dec  2 20:49:21 2023
-------------------------------

Class specified by attribute `outcome'

Read 487 cases (40 attributes) from undefined.data

Decision tree:

relationships <= 3:
:...milestones > 2:
:   :...is_web <= 0: 1 (8)
:   :   is_web > 0: 0 (6/1)
:   milestones <= 2:
:   :...age_last_milestone_year > 5:
:       :...age_last_milestone_year <= 6: 1 (5)
:       :   age_last_milestone_year > 6:
:       :   :...has_VC <= 0: 1 (4/1)
:       :       has_VC > 0: 0 (4)
:       age_last_milestone_year <= 5:
:       :...is_TX > 0:
:           :...is_software > 0: 0 (3)
:           :   is_software <= 0:
:           :   :...relationships <= 0: 0 (2)
:           :       relationships > 0: 1 (7/1)
:           is_TX <= 0:
:           :...funding_rounds > 3:
:               :...funding_rounds > 4: 0 (3)
:               :   funding_rounds <= 4:
:               :   :...city <= 63: 0 (2)
:               :       city > 63: 1 (4)
:               funding_rounds <= 3:
:               :...has_roundB <= 0: 0 (111/8)
:                   has_roundB > 0:
:                   :...first_funding_at > 2006: 0 (12)
:                       first_funding_at <= 2006:
:                       :...is_MA > 0: 0 (3)
:                           is_MA <= 0:
:                           :...avg_participants > 3: 0 (4)
:                               avg_participants <= 3:
:                               :...funding_total_usd <= 0.3048033: 0 (3)
:                                   funding_total_usd > 0.3048033: 1 (6)
relationships > 3:
:...age_last_funding_year <= 0:
    :...milestones > 2:
    :   :...is_top500 <= 0: 0 (4/1)
    :   :   is_top500 > 0: 1 (14/1)
    :   milestones <= 2:
    :   :...last_funding_at <= 2010: 0 (19/1)
    :       last_funding_at > 2010:
    :       :...is_web > 0: 0 (2)
    :           is_web <= 0:
    :           :...state_code <= 14: 0 (7/1)
    :               state_code > 14: 1 (3)
    age_last_funding_year > 0:
    :...is_MA > 0:
        :...milestones <= 3: 1 (19)
        :   milestones > 3: 0 (3/1)
        is_MA <= 0:
        :...milestones <= 0:
            :...has_roundC > 0: 1 (5)
            :   has_roundC <= 0:
            :   :...has_roundB <= 0: 0 (11/1)
            :       has_roundB > 0:
            :       :...first_funding_at <= 2005: 1 (2)
            :           first_funding_at > 2005: 0 (3/1)
            milestones > 0:
            :...founded_at <= 2003: 1 (32/2)
                founded_at > 2003:
                :...last_funding_at > 2012: 1 (9)
                    last_funding_at <= 2012:
                    :...relationships > 8:
                        :...is_top500 > 0: 1 (53/6)
                        :   is_top500 <= 0:
                        :   :...has_VC > 0: 0 (2)
                        :       has_VC <= 0:
                        :       :...is_NY > 0: 1 (3)
                        :           is_NY <= 0:
                        :           :...name <= 430: 0 (2)
                        :               name > 430: 1 (2)
                        relationships <= 8:
                        :...is_mobile > 0:
                            :...avg_participants > 3: 1 (2)
                            :   avg_participants <= 3:
                            :   :...is_otherstate <= 0:
                            :       :...funding_rounds <= 2: 0 (7)
                            :       :   funding_rounds > 2: 1 (3/1)
                            :       is_otherstate > 0:
                            :       :...name <= 450: 1 (2)
                            :           name > 450: 0 (2)
                            is_mobile <= 0:
                            :...has_roundC > 0:
                                :...age_first_funding_year > 1: 0 (3)
                                :   age_first_funding_year <= 1:
                                :   :...funding_total_usd <= 0.5674734: 1 (2)
                                :       funding_total_usd > 0.5674734: 0 (2)
                                has_roundC <= 0:
                                :...is_enterprise > 0:
                                    :...state_code <= 14: 1 (7)
                                    :   state_code > 14: 0 (3/1)
                                    is_enterprise <= 0:
                                    :...funding_total_usd > 0.5293439: 0 (5)
                                        funding_total_usd <= 0.5293439:
                                        :...age_last_milestone_year > 4:
                                            :...state_code <= 27: 1 (14)
                                            :   state_code > 27: 0 (3/1)
                                            age_last_milestone_year <= 4:
                                            :...is_advertising > 0: 1 (4/1)
                                                is_advertising <= 0:
                                                :...has_angel > 0: [S1]
                                                    has_angel <= 0: [S2]

SubTree [S1]

has_roundA > 0: 0 (5)
has_roundA <= 0:
:...avg_participants <= 3: 0 (6/1)
    avg_participants > 3: 1 (5)

SubTree [S2]

is_otherstate > 0: 1 (2)
is_otherstate <= 0:
:...funding_rounds > 2: 1 (3)
    funding_rounds <= 2:
    :...city <= 57: 1 (2)
        city > 57:
        :...is_othercategory > 0: 0 (5/1)
            is_othercategory <= 0:
            :...age_last_milestone_year > 3: 0 (5/1)
                age_last_milestone_year <= 3:
                :...has_roundB > 0: 1 (3)
                    has_roundB <= 0:
                    :...is_ecommerce > 0: 0 (2)
                        is_ecommerce <= 0:
                        :...funding_total_usd <= 0.09509083: 1 (5)
                            funding_total_usd > 0.09509083: 0 (3/1)


Evaluation on training data (487 cases):

        Decision Tree   
      ----------------  
      Size      Errors  

        62   34( 7.0%)   <<


       (a)   (b)    <-classified as
      ----  ----
       236    13    (a): class 0
        21   217    (b): class 1


    Attribute usage:

    100.00% relationships
    100.00% milestones
     61.60% age_last_funding_year
     54.83% is_MA
     49.28% age_last_milestone_year
     42.71% founded_at
     42.51% last_funding_at
     38.19% funding_rounds
     34.50% has_roundB
     32.85% is_TX
     22.59% has_roundC
     21.56% is_mobile
     17.45% funding_total_usd
     16.84% is_enterprise
     16.43% is_top500
     10.27% is_advertising
      9.45% has_angel
      9.03% is_otherstate
      8.21% avg_participants
      7.60% state_code
      6.78% first_funding_at
      6.37% city
      5.34% is_web
      4.72% is_othercategory
      3.49% has_VC
      3.29% has_roundA
      2.46% is_software
      2.05% is_ecommerce
      1.64% name
      1.44% age_first_funding_year
      1.44% is_NY


Time: 0.0 secs

The code builds the decision tree model.

B.2) Plotting

plot(C5Fit_90, type = "simple")

Root Node (Node 1):
  • Splitting attribute: “relationships” <= 3, 487 cases with 34 errors (7.0%).

Branches:
  • Nodes 2-55

Leaf Nodes (Terminal Nodes):   • Node 28: Terminal node predicting class 0 (100% class 0, 0% class 1).
  • Node 29: Terminal node predicting class 1 (42.9% class 0, 57.1% class 1).

Number of Nodes: 7 (1 root, 6 internal, 2 leaf)

Most Important Features:
  1. relationships: Critical as the root node, indicating high importance.
  2. funding_total_usd: Significant for further splits, showcasing its relevance.
  3. milestones: Used for additional splits, emphasizing its contribution.

In summary, this tree utilizes the “relationships” feature for the initial split, followed by additional criteria at each branching point. This hierarchical structure leads to predictions at the terminal nodes, where the weights associated with each terminal node indicate the number of observations falling into each category. The most important features for decision-making are relationships, funding_total_usd, and milestones. If you have any further questions or if there’s anything else I can assist you with, feel free to let me know.

B.3) Testing

predictions_GR_90 <- predict(C5Fit_90, newdata = test_data_90)
labels_GR_90 <- test_data_90$status

The code generates predictions for the test dataset using the decision tree model.

B.4) Confusion Matrix

confusion_matrix_GR_90 <- table(test_data_90$status, predictions_GR_90)
print(confusion_matrix_GR_90)
   predictions_GR_90
     0  1
  0 22  4
  1 11 26

The confusion matrix for the provided code indicates the following:

•   True Positive (TP): 26 cases were correctly predicted as 1.<br>
•   False Positive (FP): 4 cases were incorrectly predicted as 1, and 11 cases were incorrectly predicted as 0.<br>
•   True Negative (TN): 22 cases were correctly predicted as 0.<br>
•   False Negative (FN): 11 cases were incorrectly predicted as 0.<br>

B.5) Evaluation Metrics

metrics_GR_90 <- evaluate_model(predictions_GR_90, labels_GR_90)
             predictions
actual_labels  0  1
            0 22  4
            1 11 26
Accuracy: 76.19048 %
Precision: 66.66667 %
Sensitivity (Recall): 84.61538 %
Specificity: 70.27027 %

• Accuracy (76.19%): This metric represents the overall correctness of the model’s predictions. In this case, the model achieved an accuracy of 76.19%, indicating a relatively high level of correctness.

• Precision (66.67%): Precision measures the accuracy of positive predictions. In this context, the model achieved a precision of 66.67%, indicating that when it predicted a positive outcome, it was correct in 66.67% of cases.

• Sensitivity (Recall) (84.62%): Sensitivity, also known as recall, measures the ability of the model to correctly identify positive instances. In this case, the model’s sensitivity is 84.62%, suggesting a good performance in capturing positive instances.

• Specificity (70.27%): Specificity measures the ability of the model to correctly identify negative instances. With a specificity of 70.27%, the model performed moderately well in accurately identifying negative cases.

In summary, the model exhibited high accuracy and sensitivity. The precision and specificity suggest a reasonable ability to make correct predictions, both in capturing positive instances and identifying negative instances.

C) CART Algorithm: Gini Index

C.1) Constructing the Decision Tree

library(rpart)
preprocessed_dataset_ctree_GI_90 <- rpart(status ~ ., data = train_data_90, method = "class", parms = list(split = "gini"))

The code builds the decision tree model.

C.2) Plotting

print(preprocessed_dataset_ctree_GI_90)
n= 487 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

 1) root 487 238 0 (0.5112936 0.4887064)  
   2) relationships< 3.5 187  41 0 (0.7807487 0.2192513)  
     4) age_last_milestone_year< 5.5 171  31 0 (0.8187135 0.1812865)  
       8) milestones< 2.5 160  24 0 (0.8500000 0.1500000) *
       9) milestones>=2.5 11   4 1 (0.3636364 0.6363636) *
     5) age_last_milestone_year>=5.5 16   6 1 (0.3750000 0.6250000) *
   3) relationships>=3.5 300 103 1 (0.3433333 0.6566667)  
     6) age_last_funding_year< 0.5 49  19 0 (0.6122449 0.3877551)  
      12) milestones< 2.5 31   5 0 (0.8387097 0.1612903) *
      13) milestones>=2.5 18   4 1 (0.2222222 0.7777778) *
     7) age_last_funding_year>=0.5 251  73 1 (0.2908367 0.7091633) *
rpart.plot(preprocessed_dataset_ctree_GI_90)

Root Node (Node 1):
  • Splitting attribute: “relationships” < 3.5, predicting class 0 (51.1% class 0, 48.9% class 1).

Branches:
  • Node 2: If “relationships” < 3.5, predicts class 0 (78.1% class 0, 21.9% class 1).
  • Node 4: If “age_last_milestone_year” < 5.5, predicts class 0 (81.9% class 0, 18.1% class 1).
  • Node 8: If “milestones” < 2.5, predicts class 0 (85.0% class 0, 15.0% class 1).
  • Node 9: If “milestones” >= 2.5, predicts class 1 (36.4% class 0, 63.6% class 1).
  • Node 5: If “age_last_milestone_year” >= 5.5, predicts class 1 (37.5% class 0, 62.5% class 1).
  • Node 3: If “relationships” >= 3.5, predicts class 1 (34.3% class 0, 65.7% class 1).
  • Node 6: If “age_last_funding_year” < 0.5, predicts class 0 (61.2% class 0, 38.8% class 1).
  • Node 12: If “milestones” < 2.5, predicts class 0 (83.9% class 0, 16.1% class 1).
  • Node 13: If “milestones” >= 2.5, predicts class 1 (22.2% class 0, 77.8% class 1).
  • Node 7: If “age_last_funding_year” >= 0.5, predicts class 1 (29.1% class 0, 70.9% class 1).

Leaf Nodes (Terminal Nodes):
  • Node 8: Predicts class 0 (100% class 0, 0% class 1).
  • Node 9: Predicts class 1 (42.9% class 0, 57.1% class 1).

Number of Nodes: 13 (1 root, 11 internal, 2 leaf)

Most Important Features:
  1. relationships: Critical for the initial split, indicating high importance.
  2. age_last_milestone_year: Important for further splits.
  3. milestones: Significant for additional distinctions.

In summary, the tree begins with “relationships” as the key factor, followed by criteria like “age_last_milestone_year” and “milestones.” It guides predictions at the terminal nodes, where weights signify observation counts. Critical features are relationships, age_last_milestone_year, and milestones.

C.3) Testing

predictions_GI_90 <- predict(preprocessed_dataset_ctree_GI_90, newdata = test_data_90, type = "class")
labels_GI_90 <- test_data_90$status

The code generates predictions for the test dataset using the decision tree model.

C.4) Confusion Matrix

confusion_matrix_GI_90 <- table(test_data_90$status, predictions_GI_90)
print(confusion_matrix_GI_90)
   predictions_GI_90
     0  1
  0 18  8
  1  6 31

The confusion matrix for the provided code indicates the following:

•   True Positive (TP): 31 cases were correctly predicted as 1.<br>
•   False Positive (FP): 8 cases were incorrectly predicted as 1, and 6 cases were incorrectly predicted as 0.<br>
•   True Negative (TN): 18 cases were correctly predicted as 0.<br>
•   False Negative (FN): 6 cases were incorrectly predicted as 0.<br>

C.5) Evaluation Metrics

metrics_GI_90 <- evaluate_model(predictions_GI_90, labels_GI_90)
             predictions
actual_labels  0  1
            0 18  8
            1  6 31
Accuracy: 77.77778 %
Precision: 75 %
Sensitivity (Recall): 69.23077 %
Specificity: 83.78378 %

• Accuracy (77.78%): This metric represents the overall correctness of the model’s predictions. In this case, the model achieved an accuracy of 77.78%, indicating a relatively high level of correctness.

• Precision (75%): Precision measures the accuracy of positive predictions. In this context, the model achieved a precision of 75%, indicating that when it predicted a positive outcome, it was correct in 75% of cases.

• Sensitivity (Recall) (69.23%): Sensitivity, also known as recall, measures the ability of the model to correctly identify positive instances. In this case, the model’s sensitivity is 69.23%, suggesting a moderate performance in capturing positive instances.

• Specificity (83.78%): Specificity measures the ability of the model to correctly identify negative instances. With a specificity of 83.78%, the model performed well in accurately identifying negative cases.

In summary, the model exhibited high accuracy and specificity. The precision and sensitivity suggest a reasonable ability to make correct predictions, both in capturing positive instances and identifying negative instances.

5.2-Clustering

Contrary to classification, clustering is a form of unsupervised learning, where there is no predefined class label. However, in the startup data, the class attribute is already known which is the “status”. Status attribute holds two values, either “acquired” status, or “closed” status. If the class attribute is known, then what is the use clustering? It is still beneficial for exploratory data analysis to discover the unseen structure or pattern in the data set, findings of anomalies, visualizing the data set and even assessing the quality of the clustering algorithm.

We will use k-means as our partitioning method. K-means clustering is recommended for its simplicity, efficiency, and effectiveness in partitioning data into distinct groups based on similarities. It’s computationally efficient. The main goal is to partition into k numbers of clusters, where each data point belongs to the cluster with the nearest means. We chose k= 2, 3 and 4 to see what are the differences in the patterns of each k clusters and why their qualities differ from each other.

library(readxl)
preprocessed_dataset <- read_excel("Preprocessed_StartupData.xlsx")

To work on the preprocessed dataset.

library(dplyr)
preprocessed_dataset.features = preprocessed_dataset %>% select(age_first_funding_year,age_last_funding_year,age_first_milestone_year,age_last_milestone_year,relationships,funding_rounds,funding_total_usd,milestones,has_VC,has_angel,has_roundA,has_roundB,has_roundC,has_roundD,avg_participants,is_top500)
View(preprocessed_dataset.features)

First, we selected and transferred some columns from dataset to another dataset (preprocessed_dataset.features) to make it easier to cluster.

# Run k-means to find different number of clusters after omitting NA

library(ClusterR)
library(cluster)
set.seed(500)
kmeanResults<-kmeans(na.omit(preprocessed_dataset.features), 2)
kmeanResults
K-means clustering with 2 clusters of sizes 219, 508

Cluster means:
  age_first_funding_year age_last_funding_year age_first_milestone_year age_last_milestone_year relationships funding_rounds
1               1.022831              2.817352                 2.689498                4.607306     11.849315       2.502283
2               1.783465              2.751969                 2.173228                3.273622      3.690945       1.828740
  funding_total_usd milestones    has_VC has_angel has_roundA has_roundB has_roundC has_roundD avg_participants is_top500
1         0.3116405   2.575342 0.2420091 0.2420091  0.7442922  0.5433790  0.2557078 0.06392694         2.694064  0.890411
2         0.1930463   1.399606 0.3090551 0.3051181  0.3976378  0.2480315  0.1358268 0.04133858         2.464567  0.730315

Clustering vector:
  [1] 2 1 2 2 2 2 2 1 1 2 1 2 1 1 1 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 1 1 2 1 2 2 1 2 1 2 2 1 1 2 1 1 1 2 2 2 2 2 2 2 2 1 1 2 1 2 2 1 1 2
 [66] 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 1 1 1 2 1 2 2 2 2 1 1 2 2 2 2 1 1 2 2 2 1 2 1 2 1 1 2 2 2 2 2 2 1 2 1 1 2 2 2 2 2 2
[131] 1 2 2 2 2 1 2 2 2 1 2 2 1 2 1 2 2 2 2 1 2 2 2 1 2 1 2 2 1 2 1 2 2 1 2 2 2 2 2 1 2 1 2 2 2 1 2 2 1 1 2 1 2 1 2 1 1 1 2 1 1 2 2 2 2
[196] 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 1 1 2 1 2 2 2 2 2 2 1 2 2 1 1 2 2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 2 2 1 1 1 2 1 1 2 2 1 2 2 1 1 1 2 2
[261] 2 2 1 1 1 2 2 1 1 2 1 1 1 2 2 2 2 2 2 2 2 2 2 1 1 2 2 1 2 2 2 2 2 2 2 2 1 1 1 2 1 1 1 2 2 1 2 2 2 2 2 2 1 2 1 1 2 2 2 2 2 2 2 2 2
[326] 1 2 2 1 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 1 2 1 1 2 2 2 1 2 2 2 1 1 2 2 2 2 2 1 2 2 2 1 2 2 1 2 1
[391] 1 2 2 2 2 2 1 2 2 1 2 1 2 1 2 1 1 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 1 2 2 2 1 2 2 2 1 1 1 1 1 2 1 2 2 2 2 1 2 2 1 2 1 2 1 2 2
[456] 2 2 2 2 1 2 2 2 2 2 2 2 1 1 2 2 1 2 2 2 2 1 2 2 2 1 2 2 1 1 1 2 2 2 2 2 2 2 2 2 1 1 2 2 1 2 2 2 1 2 2 2 1 2 1 1 2 2 2 2 2 2 2 2 2
[521] 2 2 1 2 2 2 2 1 1 2 1 2 1 2 2 1 1 2 2 2 2 2 1 2 2 2 2 1 2 2 2 1 2 1 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 1 1 2 2 2 2 1 2 1 2 2 2 2 1 1 2
[586] 2 1 1 1 2 2 1 2 2 2 2 2 2 2 1 1 2 2 2 2 1 2 1 2 1 2 2 2 2 2 1 2 2 2 1 1 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 1 2 1 1 1 2 2 2 2 1 2
[651] 2 2 1 2 2 2 1 1 2 1 2 2 2 1 2 2 1 2 2 2 2 2 2 1 2 2 1 2 2 2 2 1 2 1 2 1 2 2 2 1 2 2 2 1 1 2 2 2 1 2 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2
[716] 2 2 1 1 1 2 2 2 1 1 1 2

Within cluster sum of squares by cluster:
[1]  6784.279 13995.556
 (between_SS / total_SS =  34.4 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss" "betweenss"    "size"         "iter"        
[9] "ifault"      
kmeanResults2<-kmeans(na.omit(preprocessed_dataset.features), 3)
kmeanResults2
K-means clustering with 3 clusters of sizes 190, 321, 216

Cluster means:
  age_first_funding_year age_last_funding_year age_first_milestone_year age_last_milestone_year relationships funding_rounds
1              0.9105263              2.763158                 2.657895                4.684211     12.368421       2.568421
2              0.6199377              1.230530                 1.183801                2.283489      4.196262       1.741433
3              3.5092593              5.069444                 3.740741                4.856481      3.578704       1.990741
  funding_total_usd milestones    has_VC  has_angel has_roundA has_roundB has_roundC has_roundD avg_participants is_top500
1         0.3157076   2.636842 0.2315789 0.23684211  0.7789474  0.5684211 0.26842105 0.05789474         2.594737 0.9000000
2         0.1158365   1.716511 0.2180685 0.47040498  0.4392523  0.1557632 0.03426791 0.01246106         2.404984 0.6822430
3         0.3201335   1.032407 0.4444444 0.05555556  0.3518519  0.4027778 0.29166667 0.09259259         2.671296 0.8148148

Clustering vector:
  [1] 3 3 2 3 2 3 3 1 1 3 1 3 1 1 1 3 3 2 2 2 3 3 2 2 3 1 2 2 2 2 3 1 2 2 1 2 2 2 2 1 2 2 1 3 3 1 1 1 3 3 3 2 2 2 3 3 1 1 2 1 2 2 1 1 3
 [66] 3 1 3 2 3 2 3 3 3 2 3 3 2 3 3 2 1 2 2 2 1 3 3 1 1 1 3 1 2 2 2 2 1 1 2 2 3 2 1 3 2 3 3 1 2 1 2 2 1 2 3 2 3 2 2 1 2 1 1 2 2 3 2 3 2
[131] 1 2 2 2 2 1 2 3 3 1 2 2 1 3 1 2 3 3 3 1 3 2 3 2 3 1 2 2 1 3 1 2 2 3 3 3 2 2 3 1 2 1 2 2 2 1 2 2 1 1 3 1 3 1 2 1 1 1 2 1 2 2 3 2 2
[196] 3 3 2 2 3 2 1 3 3 2 2 2 2 3 3 3 1 1 3 2 2 3 2 3 2 3 1 2 2 2 1 3 3 3 3 2 2 2 2 1 3 3 3 2 1 2 2 3 3 1 1 1 3 1 1 3 2 1 3 2 1 1 1 2 2
[261] 2 2 1 1 1 3 2 1 1 2 1 1 1 3 3 2 2 2 3 2 2 3 2 1 1 2 2 1 2 3 2 2 2 3 2 2 3 2 1 3 2 1 1 3 2 1 3 3 2 3 3 3 1 3 1 1 3 3 3 3 2 3 2 3 2
[326] 1 2 2 2 2 3 2 2 3 2 3 2 2 2 2 1 2 3 2 3 2 1 3 3 2 2 2 2 1 2 3 3 3 2 3 3 2 1 2 1 3 2 2 2 1 2 2 2 1 1 2 3 2 2 3 1 2 2 3 1 2 2 1 2 2
[391] 2 2 3 2 2 2 1 2 3 1 2 3 2 1 3 1 1 2 2 3 3 3 2 2 2 2 3 2 2 3 3 2 3 1 1 2 1 2 3 2 1 3 2 2 1 1 1 1 1 3 1 2 3 2 2 1 2 2 3 3 1 3 2 3 2
[456] 2 2 3 3 2 2 2 2 3 2 2 3 1 1 2 2 1 2 3 2 3 3 2 3 2 1 3 2 1 1 1 2 2 3 2 2 3 3 3 3 3 1 2 2 1 2 2 2 1 3 2 2 1 2 1 1 2 3 2 3 3 3 2 2 2
[521] 3 3 1 2 2 2 2 1 1 3 1 2 1 2 2 1 1 2 3 2 3 2 1 2 2 2 2 1 2 2 2 1 3 1 2 2 3 2 3 3 3 2 3 2 2 2 1 1 2 1 1 2 2 3 3 1 2 1 2 2 2 2 1 1 2
[586] 3 1 1 1 2 3 1 2 2 3 3 3 2 2 1 2 2 3 2 2 1 3 1 3 1 2 3 2 2 3 1 2 2 2 1 1 2 2 3 2 2 2 2 2 2 2 2 2 1 2 3 2 3 3 2 3 1 1 1 2 3 3 2 1 3
[651] 2 3 1 2 3 3 1 1 2 1 2 2 2 1 3 2 1 3 2 2 2 2 2 1 2 2 1 2 2 2 2 1 3 1 3 3 3 2 2 1 3 2 3 1 1 2 3 2 1 3 3 3 1 2 2 1 2 3 2 1 3 2 1 3 3
[716] 3 3 1 1 1 3 3 3 1 1 1 3

Within cluster sum of squares by cluster:
[1] 5352.742 5731.947 5085.621
 (between_SS / total_SS =  49.0 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss" "betweenss"    "size"         "iter"        
[9] "ifault"      
kmeanResults3<-kmeans(na.omit(preprocessed_dataset.features), 4)
kmeanResults3
K-means clustering with 4 clusters of sizes 113, 166, 200, 248

Cluster means:
  age_first_funding_year age_last_funding_year age_first_milestone_year age_last_milestone_year relationships funding_rounds
1              1.0353982              3.053097                 3.141593                5.212389     14.132743       2.654867
2              0.7228916              1.939759                 1.734940                3.536145      8.138554       2.114458
3              3.6500000              5.275000                 3.870000                4.940000      3.650000       2.015000
4              0.6572581              1.181452                 1.112903                2.048387      3.193548       1.705645
  funding_total_usd milestones    has_VC has_angel has_roundA has_roundB has_roundC  has_roundD avg_participants is_top500
1         0.3602848   2.654867 0.2389381 0.1946903  0.7876106  0.6106195 0.34513274 0.061946903         2.628319 0.9203540
2         0.1910586   2.433735 0.2349398 0.3493976  0.6927711  0.3554217 0.09638554 0.030120482         2.728916 0.8554217
3         0.3292449   1.005000 0.4500000 0.0400000  0.3250000  0.4100000 0.31500000 0.110000000         2.720000 0.8250000
4         0.1130641   1.491935 0.2177419 0.4838710  0.3870968  0.1411290 0.02822581 0.004032258         2.209677 0.6250000

Clustering vector:
  [1] 3 3 4 3 4 3 3 1 2 3 1 3 2 1 2 3 4 4 4 4 3 3 2 4 3 2 4 4 4 2 3 2 2 2 1 4 4 2 2 1 4 4 1 3 4 1 1 2 2 3 3 4 4 4 3 3 2 1 4 2 4 4 2 2 4
 [66] 3 1 3 2 3 2 3 3 3 2 3 3 4 3 3 4 1 4 4 2 1 3 3 2 2 2 3 1 4 4 2 2 1 1 4 4 3 4 2 3 4 3 3 1 2 2 4 2 1 4 3 4 3 4 4 1 4 1 1 4 4 3 4 3 4
[131] 2 2 4 2 2 2 4 3 3 1 4 4 1 3 1 4 2 3 3 1 3 4 3 2 3 1 4 4 2 3 1 4 4 3 3 3 4 2 4 1 2 1 2 4 4 1 2 4 1 2 2 2 3 1 4 1 1 2 4 1 2 4 2 2 4
[196] 4 3 4 2 3 2 1 3 3 4 4 4 2 3 3 3 2 2 3 2 4 3 4 3 4 3 1 4 4 2 2 3 3 3 3 4 4 4 4 1 3 3 3 4 2 2 4 3 3 2 2 1 3 1 2 3 4 1 3 2 2 2 1 4 4
[261] 4 4 1 1 3 3 2 2 1 4 2 1 2 2 3 4 2 4 3 4 4 3 4 1 1 2 2 1 2 3 2 4 4 3 4 4 3 2 1 3 2 1 1 3 4 2 3 3 4 3 3 3 1 3 2 2 3 3 3 2 4 3 4 3 4
[326] 1 2 4 2 4 3 4 4 3 4 3 4 4 4 4 2 4 3 4 3 4 1 3 3 4 4 2 4 1 2 3 3 3 2 3 2 4 1 4 2 3 2 2 4 1 2 4 4 2 1 2 3 4 2 3 2 4 4 4 1 4 4 1 4 2
[391] 2 4 3 4 4 4 1 4 3 1 4 3 4 1 3 1 1 4 2 2 3 3 4 4 4 4 3 4 4 3 3 4 3 1 1 4 1 4 3 4 1 3 4 4 2 2 2 2 1 3 1 2 3 2 4 2 4 4 3 3 1 3 2 3 4
[456] 4 4 3 3 2 4 4 4 3 2 2 3 1 1 4 4 2 4 3 4 3 3 4 3 2 1 3 2 1 1 1 2 4 3 4 4 3 3 3 4 3 2 4 4 1 4 2 4 2 3 2 4 1 4 1 1 4 3 4 3 3 3 2 4 4
[521] 3 3 1 4 4 2 4 1 1 3 2 4 1 4 4 3 2 4 3 4 3 2 2 2 4 2 2 2 4 4 2 1 3 2 4 4 3 4 3 3 3 4 3 4 4 4 2 1 4 1 1 4 2 3 4 1 4 2 4 4 4 4 2 2 2
[586] 2 1 2 1 4 3 2 4 4 3 2 3 4 4 2 2 4 3 2 4 1 3 1 3 2 2 3 4 4 3 1 4 4 4 2 1 4 4 3 4 4 2 2 4 4 4 2 4 2 4 3 4 3 3 2 3 1 1 2 2 3 3 4 2 3
[651] 4 3 1 4 3 3 2 1 4 1 4 4 4 2 3 4 1 3 4 4 4 4 4 1 4 4 1 2 4 4 4 1 3 2 3 3 3 4 2 1 3 4 3 3 2 4 4 4 1 3 3 3 2 4 4 1 4 3 4 1 3 4 1 3 3
[716] 3 3 1 2 2 3 3 3 1 2 2 3

Within cluster sum of squares by cluster:
[1] 2846.806 3170.094 4682.900 3758.446
 (between_SS / total_SS =  54.4 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss" "betweenss"    "size"         "iter"        
[9] "ifault"      

As said, the previous codes are to find k-mean clusters. k= 2, 3, 4 (shown in different color red in the code). In the next code snippet, you can visualize all the 2-means, 3-means and 4-means clusters.

#Visualization of all the clusters we made in the previous code snippet. 

library(factoextra)
fviz_cluster(kmeanResults, data = na.omit(preprocessed_dataset.features))


fviz_cluster(kmeanResults2, data = na.omit(preprocessed_dataset.features))


fviz_cluster(kmeanResults3, data = na.omit(preprocessed_dataset.features))

As shown, all 2-means, 3-means and 4-means clusters are overlapping. Clusters may overlap when the data doesn’t have clear boundaries or when chosen features struggle to distinguish groups effectively. If clusters are closely related or share similarities, traditional methods may find it hard to create distinct divisions. The sensitivity of algorithms to initial conditions and the subjective nature of defining clusters can also contribute to overlaps. Essentially, overlapping clusters reveal the complexity in the data, indicating the need for alternative methods or a reevaluation of feature choices to better capture underlying patterns.

A) Silhoutte Method

Silhouette method is an unsupervised method to assess the quality of the clusters. If a silhouette score is >=0.5, it means the clustering is fairly good. Ideal clustering has silhouette score = 1 while if the silhouette score is less than 0, it means wrong clustering and the sample is assigned to the wrong cluster. The Silhouette Coefficient is calculated using the mean intra-cluster distance (a) and the mean nearest-cluster distance (b) for each sample. The Silhouette score for a sample is (b - a) / max(a,b). The Silhouette width is the average of Silhouette Coefficient accross all data points in a dataset and is sometimes used interchangeably with each other.

#Silhouette method
#For 2-mean cluster and their plots
silhouette_scores<-silhouette(kmeanResults$cluster, dist(na.omit(preprocessed_dataset.features)))
plot(silhouette_scores)


#For 3-mean cluster and their plots
silhouette_scores<-silhouette(kmeanResults2$cluster, dist(na.omit(preprocessed_dataset.features)))
plot(silhouette_scores)


#For 4-mean cluster and their plots
silhouette_scores<-silhouette(kmeanResults3$cluster, dist(na.omit(preprocessed_dataset.features)))
plot(silhouette_scores)


#This is to plot the optimal K-clusters.
fviz_nbclust(na.omit(preprocessed_dataset.features), kmeans, method = "silhouette")

As you can on top, each silhouette width denoted by Si of a k-means cluster is averaged (Average silhouette width shown in the silhouette plot) and plotted on to the optimal number of cluster plot.

For example, in the silhouette plot for 2-means cluster, the silhouette coefficient (or width) is 0.29 for one cluster and 0.34 for the other cluster. When they are averaged, Average silhouette width is about 0.33. The same goes for silhouette plots for 3-means cluster (Average silhouette width = (0.29+0.31+0.21)/3= 0.27) and 4-means cluster (Average silhouette width = (0.22+0.17+0.19+0.28)/4= 0.22). This is done for many other k clusters. Then the average silhouette width is plotted accordingly onto the optimal number of clusters graph. The highest silhouette width indicates the optimal number of clusters because it is the least overlapping. In our optimal number of clusters graph, it shows that optimal number of clusters is 2. As you can see with the values of the silhouette scores, they are all less than 0.5 but greater than 0, which means the clusters must be very overlapping just like how the clustering results are shown.

B) Total Within-Cluster Sum of Square

The total within-cluster sum of square (WSS) is the sum of squared distances between each data point in a cluster and the centroid of that cluster, and then sum of these values across all clusters. The objective of the k-means algorithm is to find cluster assignments and centroids that minimize this total within-cluster sum of squares.Total Within-Cluster is found to be plotted on a graph to find the optimal numbers of cluster where the turning point is. This method is called an Elbow Method.

#Total Within-cluster sum of squares
#For 2-mean cluster
kmeanResults$tot.withinss
[1] 20779.84
#For 3-mean cluster
kmeanResults2$tot.withinss
[1] 16170.31
#For 4-mean cluster
kmeanResults3$tot.withinss
[1] 14458.25
#Elbow method found by plotting total WSS for each k-mean clusters.
fviz_nbclust(na.omit(preprocessed_dataset.features), kmeans, method = "wss")

In the graph above, it shows that the turning point is 2 which also shows that 2-mean clusters is the optimal number of clusters. The total sum of within is 20779.84 and we can see it decreases from that point making it the turning point of this graph. Again, this is called Elbow method.

C) BCubed Precision and Recall

Precision indicates the purity of a cluster. The higher the precision, the higher the purity of the cluster. Recall indicates how good the data points of same true class are put into same cluster. Below shows the steps to prepare for calculating BCubed Precision and Recall.


#Add status back to Data.features as we removed it for easiness
preprocessed_dataset.features=bind_cols(preprocessed_dataset.features,preprocessed_dataset['status'])

#Omitting NA rows since we clustered with NA values
preprocessed_dataset.features=na.omit(preprocessed_dataset.features)

#Find BCubed Precision and Recall

#1- Find number of item in cluster

kmeanResults$size
[1] 219 508
kmeanResults2$size
[1] 190 321 216
kmeanResults3$size
[1] 113 166 200 248

The 1st result shows that in the 2-means cluster, there are 2 clusters with respective number of items: 219 and 508. The 2nd result shows that in the 3-means cluster, there are 3 clusters with respective number of items: 190, 321 and 216. The 3rd result shows that in the 4-means cluster, there are 4 clusters with respective number of items: 113, 166, 200 and 248.

In the code below, we started the steps of calculating the BCubed Precision first based on acquired items which are encoded as status =‘1’. As explained not too long before, the BCubed Precision is a measure of the purity of a cluster. Purity means in our case, the more ‘acquired’ class labels are grouped together, the purer that cluster will be. If a cluster only consists of acquired items, it means the cluster has 100% precision or is 100% pure. BCubed Precision is calculated as number of acquired items divided by the total number of items in the cluster.

#3- Calculate the BCubed Precision
#BCubedPrecision = NumberOfAcquiredItems/TotalNumberOfItemsInCluster

#3.1: Precision of acquired in 2-mean clusters after finding out how many acquired items are in each cluster

TotalNumberOfItemsInCluster1=219 #Total Number of Items in Cluster 1 as found and explained in the previous code snippet.
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 1 of 2-means cluster 
acquired2Clust1<-print(sum(preprocessed_dataset.features$status[kmeanResults$cluster =="1"]== "1", na.rm = TRUE))
[1] 186
#Result of BCubed Precision
print(acquired2Clust1*100/TotalNumberOfItemsInCluster1)
[1] 84.93151
TotalNumberOfItemsInCluster=508 #Total Number of Items in Cluster 2 as found and explained in the previous code snippet.
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 2 of 2-means cluster
acquired2Clust2<-print(sum(preprocessed_dataset.features$status[kmeanResults$cluster =="2"]== "1", na.rm = TRUE))
[1] 266
#Result of BCubed Precision
print(acquired2Clust2*100/TotalNumberOfItemsInCluster)
[1] 52.3622

Results of 2-means clustering BCubed Precision for 2 clusters

Cluster No No Of Items No Of Acquired Items BCubed Precision
Cluster 1 219 186 84.93151%
Cluster 2 508 266 52.3622%

Cluster 1 is mostly pure as its precision is high while Cluster 2 is not pure as its precision is close to half which means it is almost equally mixed with acquired items and closed items.

#3.1: Precision of acquired in 3-mean clusters after finding out how many acquired items are in each cluster


TotalNumberOfItemsInCluster=190 #Total No of Items in Cluster 1 as found and explained in the code snippet before BCubed.
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 1 of 3-means cluster
acquired3Clust1<-print(sum(preprocessed_dataset.features$status[kmeanResults2$cluster =="1"]== "1", na.rm = TRUE))
[1] 164
#Result of BCubed Precision
print(acquired3Clust1*100/TotalNumberOfItemsInCluster)
[1] 86.31579
TotalNumberOfItemsInCluster=321 #Total No of Items in Cluster 2 as found and explained in the code snippet before BCubed.
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 2 of 3-means cluster
acquired3Clust2<-print(sum(preprocessed_dataset.features$status[kmeanResults2$cluster =="2"]== "1", na.rm = TRUE))
[1] 156
#Result of BCubed Precision
print(acquired3Clust2*100/TotalNumberOfItemsInCluster)
[1] 48.59813
TotalNumberOfItemsInCluster=216 #Total No of Items in Cluster 3 as found and explained in the code snippet before BCubed.
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 3 of 3-means cluster
acquired3Clust3<-print(sum(preprocessed_dataset.features$status[kmeanResults2$cluster =="3"]== "1", na.rm = TRUE))
[1] 132
#Result of BCubed Precision
print(acquired3Clust3*100/TotalNumberOfItemsInCluster)
[1] 61.11111

Results of 3-means clustering BCubed Precision for 3 clusters

Cluster No No Of Items No Of Acquired Items BCubed Precision
Cluster 1 190 164 86.31579%
Cluster 2 321 156 48.59813%
Cluster 3 216 132 61.11111%

Cluster 1 is mostly pure as precision is high. Cluster 2 is not pure at all as it is almost equally mixed with acquired and closed items. Cluster 3 is a little bit pure as it is more acquired items but not by a high percentage of precision.

#3.1: Precision of acquired in 4-mean clusters after finding out how many acquired items are in each cluster

TotalNumberOfItemsInCluster=113 #Total No of Items in Cluster 1 as found and explained in the code snippet before BCubed.
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 1 of 4-means cluster
acquired4Clust1<-print(sum(preprocessed_dataset.features$status[kmeanResults3$cluster =="1"]== "1", na.rm = TRUE))
[1] 101
#Result of BCubed Precision
print(acquired4Clust1*100/TotalNumberOfItemsInCluster)
[1] 89.38053
TotalNumberOfItemsInCluster=166 #Total No of Items in Cluster 2 as found and explained in the code snippet before BCubed.
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 2 of 4-means cluster
acquired4Clust2<-print(sum(preprocessed_dataset.features$status[kmeanResults3$cluster =="2"]== "1", na.rm = TRUE))
[1] 125
#Result of BCubed Precision
print(acquired4Clust2*100/TotalNumberOfItemsInCluster)
[1] 75.3012
TotalNumberOfItemsInCluster=200 #Total No of Items in Cluster 3 as found and explained in the code snippet before BCubed.
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 3 of 4-means cluster
acquired4Clust3<-print(sum(preprocessed_dataset.features$status[kmeanResults3$cluster =="3"]== "1", na.rm = TRUE))
[1] 124
#Result of BCubed Precision
print(acquired4Clust3*100/TotalNumberOfItemsInCluster)
[1] 62
TotalNumberOfItemsInCluster=248 #Total No of Items in Cluster 4 as found and explained in the code snippet before BCubed.
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 4 of 4-means cluster
acquired4Clust4<-print(sum(preprocessed_dataset.features$status[kmeanResults3$cluster =="4"]== "1", na.rm = TRUE))
[1] 102
#Result of BCubed Precision
print(acquired4Clust4*100/TotalNumberOfItemsInCluster)
[1] 41.12903

Results of 4-means clustering BCubed Precision for 4 clusters

Cluster No No Of Items No Of Acquired Items BCubed Precision
Cluster 1 113 101 89.38053%
Cluster 2 166 125 75.30120%
Cluster 3 200 124 62.00000%
Cluster 4 248 102 41.12903%

Cluster 1 is almost pure as it has a good amount of precision. Cluster 2 is fairly pure. Cluster 3 is a little bit pure but with a high percentage of precision. Cluster 4 is not pure at all. Rather the precision indicates that the number of acquired items is lower than the number of closed items in this cluster.

In the code below, we started the steps of calculating the BCubed Recall also based on acquired items which are encoded as status =‘1’. The BCubed Recall indicates how good the data points of same true class are put into same cluster. In our case, it is how good the acquired data points are put into the same cluster as other acquired data points. Therefore the better the recall is, the more it shows that acquired data points are put together. We calculated BCubed Recall as number of acquired items in the cluster divided by total number of acquired items in the whole dataset.

#4- Calculate the BCubed Recall
#BCubedRecall = NumberOfAcquiredItemsInCluster/TotalAcquiredItemsInDataset

#4.1- Find the number of all the rows with acquired in the class by filtering '1' in the dataset.features and seeing how many entries.
# Note that we have encoded class label 'acquired' to '1' for easy change. 
TotalAcquiredItemsInDataset = print(sum(preprocessed_dataset.features$status == "1", na.rm = TRUE))
[1] 452
#Number of 'acquired'= 452 rows

#4.2: Recall of acquired in 2-mean clusters

# This code down below calculates the number of acquired items (encoded as '1') in Cluster 1 of 2-means cluster 
acquired2Clust1<-print(sum(preprocessed_dataset.features$status[kmeanResults$cluster =="1"]== "1", na.rm = TRUE))
[1] 186
Recall1Clust1= print(acquired2Clust1*100/TotalAcquiredItemsInDataset) 
[1] 41.15044
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 2 of 2-means cluster
acquired2Clust2<-print(sum(preprocessed_dataset.features$status[kmeanResults$cluster =="2"]== "1", na.rm = TRUE))
[1] 266
Recall1Clust2= print(acquired2Clust2*100/TotalAcquiredItemsInDataset)
[1] 58.84956

Note that the first result is the total number of acquired items in the whole dataset. ### Results of 2-means clustering BCubed Recall for 2 cluster

Cluster No No of acquired Items BCubed Recall
Cluster 1 186 41.15044%
Cluster 2 266 58.84959%

For Cluster 1, the BCubed Recall is low which means only less than half of the acquired items are together in this cluster. In Cluster 2, the BCubed Recall is also low however a little more than half of the acquired items are in this cluster.

#BCubedRecall = NumberOfAcquiredItemsInCluster/TotalAcquiredItemsInDataset

#4.1- Find the number of all the rows with acquired in the class by filtering '1' in the dataset.features and seeing how many entries.
# Note that we have encoded class label 'acquired' to '1' for easy change. 
TotalAcquiredItemsInDataset = print(sum(preprocessed_dataset.features$status == "1", na.rm = TRUE))
[1] 452
#Number of 'acquired'= 452 rows

#4.3: Recall of acquired in 3-mean clusters 

# This code down below calculates the number of acquired items (encoded as '1') in Cluster 1 of 3-means cluster
acquired3Clust1<-print(sum(preprocessed_dataset.features$status[kmeanResults2$cluster =="1"]== "1", na.rm = TRUE))
[1] 164
Recall3Clust1= print(acquired3Clust1*100/TotalAcquiredItemsInDataset) 
[1] 36.28319
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 2 of 3-means cluster
acquired3Clust2<-print(sum(preprocessed_dataset.features$status[kmeanResults2$cluster =="2"]== "1", na.rm = TRUE))
[1] 156
Recall3Clust2= print(acquired3Clust2*100/TotalAcquiredItemsInDataset)
[1] 34.51327
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 3 of 3-means cluster
acquired3Clust3<-print(sum(preprocessed_dataset.features$status[kmeanResults2$cluster =="3"]== "1", na.rm = TRUE))
[1] 132
Recall3Clust3= print(acquired3Clust3*100/TotalAcquiredItemsInDataset)
[1] 29.20354

Note that TotalAcquiredItemsInDataset is the total number of acquired items in the whole dataset.

Results of 3-means clustering BCubed Recall for 3 cluster

Cluster No No of acquired Items BCubed Recall
Cluster 1 164 36.28319%
Cluster 2 156 34.51327%
Cluster 3 132 29.20354%

In Cluster 1, 2 and 3, the BCubed Recall is very low which means the acquired items are not well put together in any of the clusters.This may also indicate that 3-mean clustering does not have a good quality.

#BCubedRecall = NumberOfAcquiredItemsInCluster/TotalAcquiredItemsInDataset

#4.1- Find the number of all the rows with acquired in the class by filtering '1' in the dataset.features and seeing how many entries.
# Note that we have encoded class label 'acquired' to '1' for easy change. 
TotalAcquiredItemsInDataset = print(sum(preprocessed_dataset.features$status == "1", na.rm = TRUE))
[1] 452
#Number of 'acquired'= 452 rows

#4.4: Recall of acquired in 4-mean clusters 

# This code down below calculates the number of acquired items (encoded as '1') in Cluster 1 of 4-means cluster
acquired4Clust1<-print(sum(preprocessed_dataset.features$status[kmeanResults3$cluster =="1"]== "1", na.rm = TRUE))
[1] 101
Recall4Clust1= print(acquired4Clust1*100/TotalAcquiredItemsInDataset) 
[1] 22.34513
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 2 of 4-means cluster
acquired4Clust2<-print(sum(preprocessed_dataset.features$status[kmeanResults3$cluster =="2"]== "1", na.rm = TRUE))
[1] 125
Recall4Clust2= print(acquired4Clust2*100/TotalAcquiredItemsInDataset)
[1] 27.65487
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 3 of 4-means cluster
acquired4Clust3<-print(sum(preprocessed_dataset.features$status[kmeanResults3$cluster =="3"]== "1", na.rm = TRUE))
[1] 124
Recall4Clust3= print(acquired4Clust3*100/TotalAcquiredItemsInDataset)
[1] 27.43363
# This code down below calculates the number of acquired items (encoded as '1') in Cluster 4 of 4-means cluster
acquired4Clust4<-print(sum(preprocessed_dataset.features$status[kmeanResults3$cluster =="4"]== "1", na.rm = TRUE))
[1] 102
Recall4Clust4= print(acquired4Clust4*100/TotalAcquiredItemsInDataset)
[1] 22.56637

Note that TotalAcquiredItemsInDataset is the total number of acquired items in the whole dataset. Results of 4-means clustering BCubed Recall for 4 cluster

Cluster No No of acquired Items BCubed Recall
Cluster 1 101 22.34513%
Cluster 2 125 27.65487%
Cluster 3 124 27.43363%
Cluster 4 102 22.56637%

In Cluster 1, 2, 3 and 4, the BCubed Recall is very low, so none of the acquired items are well put together. This may also indicate that 4-mean clustering does not have a good quality.

6-Evaluation and Comparison

6.1-Classification

70:30 70:30 70:30 80:20 80:20 80:20 90:10 90:10 90:10
IG IG Ratio Gini Index IG IG Ratio Gini Index IG IG Ratio Gini Index
Accuracy 11.2% 67.46% 79.29% 26.17% 76.64% 73.83% 9.52% 76.19% 77.78%
Precision 100 % 64% 84.75% 85.19% 73.47% 71.74% 85.71% 66.67% 69.23%
Sensitivity 34.2 % 63.16% 65.79% 74.19% 75% 68.75% 75% 84.62% 69.23%
Specificity 100 % 70.97% 90.32% 55.56% 77.97% 77.97% 0% 70.27% 83.78%
  1. Information Gain (IG) and Gini Index: Higher values for IG and lower values for the Gini Index are desirable. For 70:30 split, Gini Index has the lowest value (79.29%), indicating better separation.
  2. Accuracy, Precision, Sensitivity, and Specificity: Higher accuracy, precision, sensitivity, and specificity are desired. For 70:30 split, Gini Index achieves the highest accuracy (79.29%), precision (84.75%), sensitivity (65.79%), and specificity (90.32%).

We evaluated the performance of Information Gain, Gain Ratio, and Gini Index across three partitions (70:30, 80:20, and 90:10) by calculating accuracy, precision, sensitivity, and specificity. With a balanced dataset, we relied on accuracy as the primary metric to judge algorithm performance. The Gini Index for the 70:30 partition emerged as the best-performing algorithm, emphasizing that more nodes don’t necessarily lead to better accuracy. Despite having only 8 nodes and testing three features, the Gini Index achieved superior accuracy.

Across the three partitions, Information Gain yielded the lowest average accuracy (15.63%), while Gain Ratio showed an average accuracy of 73.43%. Notably, the Gini Index outperformed both, boasting an average accuracy of 76.97%. In addition, the 70:30 split struck a balance that resulted in the best accuracy, as observed through the Gini Index, Information Gain, and Gain Ratio.

Considering these factors, the 70:30 split with Gini Index is the most optimal choice for classification.

6.2-Clustering

K = 2 K = 3 K = 4
Average Silhouette width 0.33 0.27 0.22
Total within-cluster sum of square 20779.84 16170.31 14458.25
BCubed precision 68.6% 65.3% 66.9%
BCubed recall 50.0% 33.3% 25%
Visualization
  1. Average Silhouette Width: Higher values are better, indicating better-defined clusters. Therefore, for K = 2, it has the highest value (0.33), suggesting better separation.
  2. Total Within-Cluster Sum of Square: Lower values are better, indicating compact clusters. K = 4 has the lowest value (14458.25). 3. BCubed Precision and Recall: Higher precision and recall are desirable. Looking at K = 2, it has a good balance between precision (68.6%) and recall (50%).

Considering these factors, 2-means seems to be the most optimal choice for clustering.

7-Results and Findings

To summarize:

• Best Clustering Algorithm: K-means with K = 2.
• Best Classification Algorithm: Decision tree with a 70:30 split using the Gini Index.

For a small dataset with 727 rows, the choice between the clustering algorithm (K-means with K = 2) and the classification algorithm (Decision tree with Gini Index, 70:30 split) depends on several factors:

  1. Size of the Dataset: K-means can be computationally efficient for startup dataset which is small. However, decision trees, especially with a limited depth, can also handle smaller datasets effectively.

  2. Nature of the Data: Because the startup data doesn’t form distinct clusters, K-means is not recommended. However, there are clear patterns and relationships between features that was captured by the 70:30 decision tree.

  3. Interpretability: Decision trees are often more interpretable, providing insights into the decision-making process, which can be valuable in understanding the data.

  4. Computational Resources: K-means is generally computationally efficient, but the dataset size is still relatively small.

Given these considerations, because interpretability and understanding the decision process are important, and the startup dataset is not extremely large, the Decision Tree with Gini Index and a 70:30 split the ideal choice for the startup dataset.

LS0tCnRpdGxlOiAiU3RhcnR1cCBTdWNjZXNzIFByZWRpY3Rpb24gTW9kZWwiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KPGJyPgpJbnRyb2R1Y3Rpb24gdG8gRGF0YSBNaW5pbmcgKElUMzI2KSBDb3Vyc2UgUHJvamVjdCAKKipQcmVwYXJlZCBieToqKiBCYXNtYSBBbHN1bGFpbSBhbmQgQWFmaWEgTmF3YWwgTXVoYW1tYWQgCgojIyAxLSBQcm9ibGVtIAoKJm5ic3A7Jm5ic3A7Jm5ic3A7IFRoZSBwcm9ibGVtIHdlIGFpbSB0byBhZGRyZXNzIHJldm9sdmVzIGFyb3VuZCBwcmVkaWN0aW5nIHRoZSBzdWNjZXNzIG9mIHN0YXJ0dXBzIGJhc2VkIG9uIGhpc3RvcmljYWwgZGF0YSBlbmNvbXBhc3NpbmcgdGhlaXIgZWFybHktc3RhZ2UgZGVjaXNpb25zLiBUaGUgZGF0YXNldCBwcm92aWRlcyBhIGNvbXByZWhlbnNpdmUgcmVjb3JkIG9mIHN0YXJ0dXAgY29tcGFuaWVzLCB0cmFjaW5nIHRoZWlyIGVhcmx5LXN0YWdlIGRlY2lzaW9ucywgc3VjaCBhcyBmdW5kaW5nIHJvdW5kcywgY2F0ZWdvcmllcywgYW5kIG1vcmUsIHNwYW5uaW5nIHRoZSB5ZWFycyBmcm9tIDE5ODQgdG8gMjAxMC4gV2Ugc3RyaXZlIHRvIGJ1aWxkIGEgcHJlZGljdGl2ZSBtb2RlbCwgcGFydGljdWxhcmx5IHVzaW5nIERlY2lzaW9uIFRyZWVzLCB0byBhbnRpY2lwYXRlIHRoZSBvdXRjb21lIG9mIHN0YXJ0dXBzLiAKCiZuYnNwOyZuYnNwOyZuYnNwOyBUaGUgYWJpbGl0eSB0byBwcmVkaWN0IHN0YXJ0dXAgc3VjY2VzcyBpcyBpbXBvcnRhbnQgZm9yIGJvdGggaW52ZXN0b3JzIGFuZCBqb2Igc2Vla2Vycy4gRm9yIGludmVzdG9ycywgaXQgc2VydmVzIGFzIGEgc3RyYXRlZ2ljIHRvb2wgdG8gaWRlbnRpZnkgc3RhcnR1cHMgd2l0aCBhIGhpZ2hlciBsaWtlbGlob29kIG9mIHN1Y2Nlc3MsIGxlYWRpbmcgdG8gbW9yZSBpbmZvcm1lZCBpbnZlc3RtZW50IGRlY2lzaW9ucyBhbmQgYmV0dGVyIHJldHVybnMuIE9uIHRoZSBmbGlwIHNpZGUsIGpvYiBzZWVrZXJzIGNhbiBiZW5lZml0IGJ5IGlkZW50aWZ5aW5nIHByb21pc2luZyBjb21wYW5pZXMsIGluY3JlYXNpbmcgdGhlIGxpa2VsaWhvb2Qgb2YgYSBmcnVpdGZ1bCBhbmQgc3RhYmxlIGNhcmVlciBwYXRoLjxicj48YnI+PGJyPgoKIyMgMi0gRGF0YSBNaW5pbmcgVGFzawoKJm5ic3A7Jm5ic3A7Jm5ic3A7IFRoZSBkYXRhIG1pbmluZyB0YXNrIGludm9sdmVzIHR3byBrZXkgYXNwZWN0czogY2xhc3NpZmljYXRpb24gYW5kIGNsdXN0ZXJpbmcuIEluIHRoZSBjbGFzc2lmaWNhdGlvbiBwYXJ0LCB0aGUgZ29hbCBpcyB0byBjcmVhdGUgYSBwcmVkaWN0aXZlIG1vZGVsIHRoYXQgc29ydHMgc3RhcnR1cHMgaW50byAnYWNxdWlyZWQnIG9yICdjbG9zZWQnIGNhdGVnb3JpZXMgdXNpbmcgdGhlIGNsYXNzIGxhYmVsICdzdGF0dXMnLiBPbiB0aGUgY2x1c3RlcmluZyBwYXJ0LCB0aGUgYWltIGlzIHRvIHVuY292ZXIgcGF0dGVybnMgYW5kIHN0cnVjdHVyZXMgd2l0aGluIHRoZSBkYXRhc2V0LCBpZGVudGlmeWluZyBncm91cHMgb2Ygc3RhcnR1cHMgd2l0aCBzaW1pbGFyIHRyYWl0cy4gVGhpcyBkdWFsIGFwcHJvYWNoIGFpbXMgdG8gcHJvdmlkZSBhIGNvbXByZWhlbnNpdmUgdW5kZXJzdGFuZGluZyBvZiBzdGFydHVwIG91dGNvbWVzLCBvZmZlcmluZyBwcmVkaWN0aXZlIGluc2lnaHRzIGFuZCByZXZlYWxpbmcgdW5kZXJseWluZyBzdHJ1Y3R1cmVzIGluIHRoZSBzdGFydHVwIGRhdGFzZXQuCgojIyMjIEdvYWxzOgoKMS4gKipQcmVkaWN0aW9uOioqIENyZWF0ZSBhIHByZWRpY3RpdmUgbW9kZWwgdG8gcHJlZGljdCBpZiBhIHN0YXJ0dXAgd2lsbCBzdWNjZWVkIG9yIGNsb3NlLCB1c2luZyBwYXN0IGRhdGEgZm9yIGJldHRlciBkZWNpc2lvbi1tYWtpbmcuICAKCjIuICoqUGF0dGVybiBEaXNjb3Zlcnk6KiogRmluZCBoaWRkZW4gcGF0dGVybnMgaW4gc3RhcnR1cCBkYXRhLCBoZWxwaW5nIHVzIHVuZGVyc3RhbmQgY29tbW9uIHRyYWl0cyBhbW9uZyB0aGVtLgoKMy4gKipTbWFydCBJbnZlc3Rpbmc6KiogQXNzaXN0IGludmVzdG9ycyBpbiBtYWtpbmcgc3RyYXRlZ2ljIGRlY2lzaW9ucyBieSBpZGVudGlmeWluZyBzdGFydHVwcyB3aXRoIGEgaGlnaCBjaGFuY2Ugb2Ygc3VjY2VzcywgbGVhZGluZyB0byBiZXR0ZXIgcmV0dXJucy48YnI+PGJyPjxicj4KCgojIyAzLSBEYXRhIERlc2NyaXB0aW9uCgombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsqKuKXhiBTb3VyY2U6KiogQWNjZXNzIHRoZSBkYXRhc2V0IG9uIEthZ2dsZSB0aHJvdWdoIHRoZSBmb2xsb3dpbmcgbGluazogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9tYW5pc2hrYzA2L3N0YXJ0dXAtc3VjY2Vzcy1wcmVkaWN0aW9uICAgCgombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsqKuKXhiBOdW1iZXIgb2Ygb2JqZWN0czoqKiBUaGUgZGF0YXNldCBob2xkcyA5MjUgcm93cyBvZiBkYXRhIGJlZm9yZSBwcmVwcm9jZXNzaW5nLgoKJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Kiril4YgTnVtYmVyIG9mIGF0dHJpYnV0ZXM6KiogVGhlIGRhdGFzZXQgaG9sZHMgNTAgY29sdW1ucyBvZiBkYXRhIGJlZm9yZSBwcmVwcm9jZXNzaW5nLgoKJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Kiril4YgQ2xhc3MgbGFiZWw6KiogVGhlIGNsYXNzIGxhYmVsIGZvciB0aGlzIGRhdGEgc2V0IGlzICoqc3RhdHVzKiosIHdoaWNoIGhvbGRlcyB0d28gc3RhdGVzOiAiYWNxdWlyZWQiIChpLmUuIHN1Y2Nlc3NmdWwgY29tcGFueSkgb3IgImNsb3NlZCIgKGkuZS4gdW5zdWNjZXNzZnVsIGNvbXBhbnkpLgoKJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Kiril4YgVHlwZXMgb2YgYXR0cmlidXRlczoqKiBOb21pbmFsLCBOdW1lcmljYWwsIEJpbmFyeTxicj48YnI+CgojIyMgRGF0YSBEaXNjdGlvbmFyeTogCgp8IEF0dHJpYnV0ZSBOYW1lICAgICAgICAgICB8IERlc2NyaXB0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IFR5cGUgICAgICB8IFBvc3NpYmxlIFZhbHVlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8CnwgVW5uYW1lZDogMCAgICAgICAgICAgICAgIHwgTWV0aG9kIG9mIG51bWJlcmluZyBjb21wYW5pZXMuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgTm9taW5hbCAgIHwgMSB0byAxMTUzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IHN0YXRlX2NvZGUgICAgICAgICAgICAgICB8IFRoZSBjb2RlIG9mIHRoZSBzdGF0ZSB0aGUgc3RhcnR1cCB3YXMgZm91bmRlZCBpbi4gICAgICAgICAgICB8IE5vbWluYWwgICB8IERpZmZlcmVudCBzdGF0ZSBjb2Rlcy4gTGlrZSBDQSwgQVogICAgICAgICAgICAgIHwKfCBsYXRpdHVkZSAgICAgICAgICAgICAgICAgfCBUaGUgbGF0aXR1ZGUgb2YgdGhlIHN0YXJ0dXAgaGVhZHF1YXJ0ZXJzLiAgICAgICAgICAgICAgICAgICAgfCBOdW1lcmljYWwgfCA5MCB0byAtOTAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgbG9uZ2l0dWRlICAgICAgICAgICAgICAgIHwgVGhlIGxvbmdpdHVkZSBvZiB0aGUgc3RhcnR1cCBoZWFkcXVhcnRlcnMuICAgICAgICAgICAgICAgICAgIHwgTnVtZXJpY2FsIHwgMCB0byAxODAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IHppcF9jb2RlICAgICAgICAgICAgICAgICB8IFRoZSB6aXAgY29kZSBvZiB0aGUgY2l0eSB0aGUgc3RhcnR1cCB3YXMgZm91bmRlZCBpbi4gICAgICAgICB8IE5vbWluYWwgICB8IERpZmZlcmVudCByYW5kb20gY2l0eSB6aXBjb2RlcyAgICAgICAgICAgICAgICAgIHwKfCBpZCAgICAgICAgICAgICAgICAgICAgICAgfCBJcnJlbGV2YW50XCogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBOb21pbmFsICAgfCBEYXRhIGluIHVucmVjb2duaXphYmxlIGZvcm1hdCBjOm51bWJlciAgICAgICAgICB8CnwgY2l0eSAgICAgICAgICAgICAgICAgICAgIHwgVGhlIG5hbWUgb2YgdGhlIGNpdHkgdGhlIHN0YXJ0dXAgd2FzIGZvdW5kZWQgaW4uICAgICAgICAgICAgIHwgTm9taW5hbCAgIHwgRGlmZmVyZW50IGNpdGllcy4gTGlrZSBQYWxvIEFsdG8sIE1vdW50YWluIFZpZXcgfAp8IFVubmFtZWQ6IDYgICAgICAgICAgICAgICB8IFRoZSBhZGRyZXNzIG9mIHRoZSBoZWFkcXVhcnRlcnMuICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IE5vbWluYWwgICB8IERpZmZlcmVudCBhZGRyZXNzZXMuIExpa2UgU2FuIERpZWdvIENBIDkyMTIxICAgIHwKfCBuYW1lICAgICAgICAgICAgICAgICAgICAgfCBUaGUgbmFtZSBvZiB0aGUgc3RhcnR1cC4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBOb21pbmFsICAgfCBEaWZmZXJlbnQgbmFtZXMuIExpa2UgUXNlY3VyZSwgZHJvcC5pbyAgICAgICAgICB8CnwgbGFiZWxzICAgICAgICAgICAgICAgICAgIHwgSXJyZWxldmFudFwqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQmluYXJ5ICAgIHwgMCBvciAxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGZvdW5kZWRfYXQgICAgICAgICAgICAgICB8IFN0YXJ0dXAgZm91bmRpbmcgZGF0ZS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IE5vbWluYWwgICB8IDAxIDE5ODQgdG8gMDkgMjAxMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBjbG9zZWRfYXQgICAgICAgICAgICAgICAgfCBTdGFydHVwIGNsb3NpbmcgZGF0ZS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBOb21pbmFsICAgfCAwMSAyMDAxIHRvIDA4IDIwMTMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgZmlyc3RfZnVuZGluZ19hdCAgICAgICAgIHwgU3RhcnR1cCBmaXJzdCBmdW5kaW5nIGRhdGUuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgTm9taW5hbCAgIHwgMDEgMjAwMCB0byAwOSAyMDA5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGxhc3RfZnVuZGluZ19hdCAgICAgICAgICB8IFN0YXJ0dXAgbGFzdCBmdW5kaW5nIGRhdGUuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IE5vbWluYWwgICB8IDAxIDIwMDEgdG8gMDkgMjAxMSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBhZ2VfZmlyc3RfZnVuZGluZ195ZWFyICAgfCBUaGUgYXZlcmFnZSBhZ2Ugb2Ygc3RhcnR1cCB3aGVuIHJlY2VpdmVkIGZpcnN0IGZ1bmRpbmcuICAgICAgfCBOdW1lcmljYWwgfCAwIHRvIDIxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYWdlX2xhc3RfZnVuZGluZ195ZWFyICAgIHwgVGhlIGF2ZXJhZ2UgYWdlIG9mIHN0YXJ0dXAgd2hlbiByZWNlaXZlZCBsYXN0IGZ1bmRpbmcuICAgICAgIHwgTnVtZXJpY2FsIHwgMCB0byAyMSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGFnZV9maXJzdF9taWxlc3RvbmVfeWVhciB8IFRoZSBhdmVyYWdlIGFnZSBvZiBzdGFydHVwIHdoZW4gYWNoZWl2ZWQgZmlyc3QgbWlsZXN0b25lLiAgICB8IE51bWVyaWNhbCB8IDAgdG8gMjQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBhZ2VfbGFzdF9taWxlc3RvbmVfeWVhciAgfCBUaGUgYXZlcmFnZSBhZ2Ugb2Ygc3RhcnR1cCB3aGVuIGFjaGVpdmVkIGxhc3QgbWlsZXN0b25lLiAgICAgfCBOdW1lcmljYWwgfCAwIHRvIDI0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgcmVsYXRpb25zaGlwcyAgICAgICAgICAgIHwgSXJyZWxldmFudFwqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgTnVtZXJpY2FsIHwgMCB0byA2MyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGZ1bmRpbmdfcm91bmRzICAgICAgICAgICB8IFRoZSBudW1iZXIgb2YgZnVuZGluZyByb3VuZHMgdGhlIHN0YXJ0dXAgd2VudCB0aHJvdWdoLiAgICAgICB8IE51bWVyaWNhbCB8IDEgdG8gMTAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBmdW5kaW5nX3RvdGFsX3VzZCAgICAgICAgfCBUaGUgdG90YWwgbnVtYmVyIG9mIG1vbmV5IGluIFVTRCByYWlzZWQgYnkgdGhlIHN0YXJ0dXAuICAgICAgfCBOdW1lcmljYWwgfCAxMTAwMCB0byA1NzAwMDAwMDAwICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgbWlsZXN0b25lcyAgICAgICAgICAgICAgIHwgVGhlIHRvdGFsIG51bWJlciBvZiBtaWxlc3RvbmVzIGFjaGlldmVkIGJ5IHRoZSBzdGFydHVwLiAgICAgIHwgTnVtZXJpY2FsIHwgMCB0byA4ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IHN0YXRlX2NvZGUuMSAgICAgICAgICAgICB8IFRoZSBjb2RlIG9mIHRoZSBzdGF0ZSB0aGUgc3RhcnR1cCB3YXMgZm91bmRlZCBpbi4gICAgICAgICAgICB8IE5vbWluYWwgICB8IERpZmZlcmVudCBzdGF0ZSBjb2Rlcy4gTGlrZSBDQSwgQVogICAgICAgICAgICAgIHwKfCBpc19DQSAgICAgICAgICAgICAgICAgICAgfCBJZiB0aGUgc3RhcnR1cCB3YXMgZm91bmRlZCBpbiBDYWxpZm9ybmlhLiAgICAgICAgICAgICAgICAgICAgfCBCaW5hcnkgICAgfCAwIG9yIDEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgaXNfTlkgICAgICAgICAgICAgICAgICAgIHwgSWYgdGhlIHN0YXJ0dXAgd2FzIGZvdW5kZWQgaW4gTmV3IFlvcmsuICAgICAgICAgICAgICAgICAgICAgIHwgQmluYXJ5ICAgIHwgMCBvciAxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGlzX01BICAgICAgICAgICAgICAgICAgICB8IElmIHRoZSBzdGFydHVwIHdhcyBmb3VuZGVkIGluIE1hc3NhY2h1c2V0dHMuICAgICAgICAgICAgICAgICB8IEJpbmFyeSAgICB8IDAgb3IgMSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBpc19UWCAgICAgICAgICAgICAgICAgICAgfCBJZiB0aGUgc3RhcnR1cCB3YXMgZm91bmRlZCBpbiBUZXhhcy4gICAgICAgICAgICAgICAgICAgICAgICAgfCBCaW5hcnkgICAgfCAwIG9yIDEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgaXNfb3RoZXJzdGF0ZSAgICAgICAgICAgIHwgSWYgdGhlIHN0YXJ0dXAgd2FzIGZvdW5kZWQgaW4gYSBzdGF0ZSBvdGhlciB0aGFuIHRoZSBsaXN0ZWQuIHwgQmluYXJ5ICAgIHwgMCBvciAxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGNhdGVnb3J5X2NvZGUgICAgICAgICAgICB8IFRoZSBzZWN0b3Igb2YgdGhlIHN0YXJ0dXAuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IE5vbWluYWwgICB8IERpZmZlcmVudCBjYXRlZ29yaWVzLiBMaWtlIGJpb3RlY2gsIG1vYmlsZSAgICAgIHwKfCBpc19zb2Z0d2FyZSAgICAgICAgICAgICAgfCBJZiB0aGUgc3RhcnR1cCBzZWN0b3IgaXMgc29mdHdhcmUuICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCaW5hcnkgICAgfCAwIG9yIDEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgaXNfd2ViICAgICAgICAgICAgICAgICAgIHwgSWYgdGhlIHN0YXJ0dXAgc2VjdG9yIGlzIHdlYi4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQmluYXJ5ICAgIHwgMCBvciAxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGlzX21vYmlsZSAgICAgICAgICAgICAgICB8IElmIHRoZSBzdGFydHVwIHNlY3RvciBpcyBtb2JpbGUuICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEJpbmFyeSAgICB8IDAgb3IgMSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBpc19lbnRlcnByaXNlICAgICAgICAgICAgfCBJZiB0aGUgc3RhcnR1cCBzZWN0b3IgaXMgZW50ZXJwcmlzZS4gICAgICAgICAgICAgICAgICAgICAgICAgfCBCaW5hcnkgICAgfCAwIG9yIDEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgaXNfYWR2ZXJ0aXNpbmcgICAgICAgICAgIHwgSWYgdGhlIHN0YXJ0dXAgc2VjdG9yIGlzIGFkdmVydGlzaW5nLiAgICAgICAgICAgICAgICAgICAgICAgIHwgQmluYXJ5ICAgIHwgMCBvciAxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGlzX2dhbWVzdmlkZW8gICAgICAgICAgICB8IElmIHRoZSBzdGFydHVwIHNlY3RvciBpcyB2aWRlbyBnYW1lcy4gICAgICAgICAgICAgICAgICAgICAgICB8IEJpbmFyeSAgICB8IDAgb3IgMSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBpc19lY29tbWVyY2UgICAgICAgICAgICAgfCBJZiB0aGUgc3RhcnR1cCBzZWN0b3IgaXMgZWNvbW1lcmNlLiAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCaW5hcnkgICAgfCAwIG9yIDEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgaXNfYmlvdGVjaCAgICAgICAgICAgICAgIHwgSWYgdGhlIHN0YXJ0dXAgc2VjdG9yIGlzIGJpb3RlY2guICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQmluYXJ5ICAgIHwgMCBvciAxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGlzX2NvbnN1bHRpbmcgICAgICAgICAgICB8IElmIHRoZSBzdGFydHVwIHNlY3RvciBpcyBjb25zdWx0aW5nLiAgICAgICAgICAgICAgICAgICAgICAgICB8IEJpbmFyeSAgICB8IDAgb3IgMSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBpc19vdGhlcmNhdGVnb3J5ICAgICAgICAgfCBJZiB0aGUgc3RhcnR1cCBzZWN0b3IgaXMgYW55IG90aGVyIGNhdGVnb3J5IHRoYXQgdGhlIGxpc3RlZC4gfCBCaW5hcnkgICAgfCAwIG9yIDEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8Cnwgb2JqZWN0X2lkICAgICAgICAgICAgICAgIHwgSXJyZWxldmFudFwqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgTm9taW5hbCAgIHwgRGF0YSBpbiB1bnJlY29nbml6YWJsZSBmb3JtYXQgYzpudW1iZXIgICAgICAgICAgfAp8IGhhc19WQyAgICAgICAgICAgICAgICAgICB8IElmIHRoZSBzdGFydHVwIGhhcyBhIHZlbnR1cmUgY2FwaXRhbGlzdFwqXCogaW52ZXN0b3IuICAgICAgICB8IEJpbmFyeSAgICB8IDAgb3IgMSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBoYXNfYW5nZWwgICAgICAgICAgICAgICAgfCBJZiB0aGUgc3RhcnR1cCBoYXMgYW4gYW5nZWxcKlwqXCogaW52ZXN0b3IuICAgICAgICAgICAgICAgICAgfCBCaW5hcnkgICAgfCAwIG9yIDEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgaGFzX3JvdW5kQSAgICAgICAgICAgICAgIHwgSWYgdGhlIHN0YXJ0dXAgd2VudCB0aHJvdWdoIGEgc2VyaWVzIEEgZnVuZGluZyByb3VuZC4gICAgICAgIHwgQmluYXJ5ICAgIHwgMCBvciAxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGhhc19yb3VuZEIgICAgICAgICAgICAgICB8IElmIHRoZSBzdGFydHVwIHdlbnQgdGhyb3VnaCBhIHNlcmllcyBCIGZ1bmRpbmcgcm91bmQuICAgICAgICB8IEJpbmFyeSAgICB8IDAgb3IgMSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBoYXNfcm91bmRDICAgICAgICAgICAgICAgfCBJZiB0aGUgc3RhcnR1cCB3ZW50IHRocm91Z2ggYSBzZXJpZXMgQyBmdW5kaW5nIHJvdW5kLiAgICAgICAgfCBCaW5hcnkgICAgfCAwIG9yIDEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgaGFzX3JvdW5kRCAgICAgICAgICAgICAgIHwgSWYgdGhlIHN0YXJ0dXAgd2VudCB0aHJvdWdoIGEgc2VyaWVzIEQgZnVuZGluZyByb3VuZC4gICAgICAgIHwgQmluYXJ5ICAgIHwgMCBvciAxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGF2Z19wYXJ0aWNpcGFudHMgICAgICAgICB8IFRoZSBhdmVyYWdlIG51bWJlciBvZiBwYXJ0aWNpcGFudHMgaW4gdGhlIHN0YXJ0dXAuICAgICAgICAgICB8IEJpbmFyeSAgICB8IDAgb3IgMSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBpc190b3A1MDAgICAgICAgICAgICAgICAgfCBJZiB0aGUgc3RhcnR1cCBpcyBhIHRvcCA1MDAgY29tcGFueS4gICAgICAgICAgICAgICAgICAgICAgICAgfCBCaW5hcnkgICAgfCAwIG9yIDEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8Cnwgc3RhdHVzICAgICAgICAgICAgICAgICAgIHwgVGhlIGFjcXVpc2l0aW9uIHN0YXR1cyBvZiB0aGUgc3RhcnR1cC4gICAgICAgICAgICAgICAgICAgICAgIHwgQmluYXJ5ICAgIHwgImFjcXVpcmVkIiBvciAiY2xvc2VkIiAgICAgICAgICAgICAgICAgICAgICAgICAgfAoKXCpUaGUgb3JpZ2luYWwgZGF0YXNldCBzb3VyY2UgbGFja3MgYW55IGRlc2NyaXB0aW9uIGZvciB0aGVzZSBhdHRyaWJ1dGVzLiBXZSBjYW4gb25seSBzcGVjdWxhdGUgdGhlaXIgbWVhbmluZ3MuIFRoZXkgY291bGQgcmVmZXIgdG8gdGhlIG51bWJlciBvZiBkYXRhIG9iamVjdHMsIGEgbWV0aG9kIGZvciBvcmdhbml6aW5nIGRhdGEsIG9yIGEgcmVjb3JkaW5nIHN5c3RlbSBvZiBzb21lIHNvcnQuIEZ1cnRoZXJtb3JlLCBzaW5jZSB0aGV5IHBvc2Ugbm8gaW1wb3J0YW5jZSB0byB0aGUgc3RhcnR1cCBwcmVkaWN0aW9uIG1vZGVsLCB3ZSB3aWxsIGRlc2lnbmF0ZSB0aGVtIGFzICoqImlycmVsZXZhbnQiKiogYW5kIHByb2NlZWQgdG8gcmVtb3ZlIHRoZW0gZnJvbSB0aGUgZGF0YXNldCBpbiB0aGUgc3Vic2VxdWVudCBzdGVwcy4gCgpcKlwqKipWQyoqIHN0YW5kcyBmb3IgVmVudHVyZSBDYXBpdGFsaXN0LiBXaGljaCBpcyBhIHR5cGUgb2YgaW52ZXN0b3IgdGhhdCBpbnZlc3RzIHRoZSBtb25leSBvZiBhIHZlbnR1cmUgY2FwaXRhbCBmaXJtIGludG8gc21hbGwgc3RhcnR1cCBjb21wYW5pZXMuCgpcKlwqXCoqKkFuZ2VsKiogc3RhbmRzIGZvciBBbmdlbCBJbnZlc3Rvci4gV2hpY2ggaXMgYSB0eXBlIG9mIGludmVzdG9yIHRoYXQgaW52ZXN0cyB0aGVpciBwZXJzb25hbCBtb25leSAgaW50byBzbWFsbCBzdGFydHVwIGNvbXBhbmllcyBpbiBleGNoYW5nZSBmb3IgYSBwZXJjZW50YWdlIGluIHRoZSBjb21wYW55Ljxicj4KCjxicj4KIyMjIEluc3RhbGxpbmcgbmVjZXNzYXJ5IHBhY2thZ2VzOiBQcmVwcm9jZXNzaW5nIGFuZCBWaXN1YWxpemF0aW9uCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKQppbnN0YWxsLnBhY2thZ2VzKCJsdWJyaWRhdGUiKQppbnN0YWxsLnBhY2thZ2VzKCJzY2F0dGVycGxvdDNkIikgCmluc3RhbGwucGFja2FnZXMoImRwbHlyIikKaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCmluc3RhbGwucGFja2FnZXMoImNvbG9yc3BhY2UiKQppbnN0YWxsLnBhY2thZ2VzKCJwbmciKQpgYGAKPGJyPgojIyMjIyBMb2FkIG5lY2Vzc2FyeSBwYWNrYWdlczogQ2xhc3NpZmljYXRpb24gCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCJwYXJ0eWtpdCIpCmluc3RhbGwucGFja2FnZXMoInJwYXJ0IikKaW5zdGFsbC5wYWNrYWdlcygicnBhcnQucGxvdCIpCmluc3RhbGwucGFja2FnZXMoIlJPU0UiKQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpCmluc3RhbGwucGFja2FnZXMoIkM1MCIpCmxpYnJhcnkocGFydHkpCmxpYnJhcnkocGFydHlraXQpCmxpYnJhcnkocnBhcnQucGxvdCkKbGlicmFyeShSV2VrYSkKbGlicmFyeShjYXJldCkKbGlicmFyeShycGFydCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGxhdHRpY2UpCmBgYAoKIyMjIExvYWQgbmVjZXNzYXJ5IHBhY2thZ2VzOiBDbGFzc2lmaWNhdGlvbiAKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoImRwbHlyIikKaW5zdGFsbC5wYWNrYWdlcygiQ2x1c3RlclIiKQppbnN0YWxsLnBhY2thZ2VzKCJjbHVzdGVyIikgIyBUbyBtYWtlIGNsdXN0ZXJzCmluc3RhbGwucGFja2FnZXMoImZhY3RvZXh0cmEiKSAjIFRvIHZpc3VhbGl6ZSBhbmQgdmFsaWRhdGUgdGhlIGNsdXN0ZXJzCmBgYAoKCiMjIyMgSW1wb3J0IERhdGFzZXQKYGBge3J9CmxpYnJhcnkocmVhZHhsKQpkYXRhc2V0IDwtIHJlYWRfZXhjZWwoIk9yaWdpbmFsX1N0YXJ0dXBEYXRhLnhsc3giKQpWaWV3KGRhdGFzZXQpCmBgYApUbyBpbXBvcnQgYW5kIGxvYWQgdGhlIGV4Y2VsIGRhdGFzZXQgZm9yIGRhdGEgcHJlcHJvY2Vzc2luZyBhbmQgbWluaW5nLiA8YnI+PGJyPgoKCiMjIyBBc3Nlc3NpbmcgQ2xhc3MgTGFiZWwgQmFsYW5jZSBCZWZvcmUgUHJlLVByb2Nlc3NpbmcgCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdyaWQpCgojIENyZWF0ZSBhIGJhciBwbG90IGZvciB0aGUgInN0YXR1cyIgYXR0cmlidXRlCmdnIDwtIGdncGxvdChkYXRhc2V0LCBhZXMoeCA9IHN0YXR1cykpICsKICBnZW9tX2JhcihmaWxsID0gImRhcmtncmF5IiwgY29sb3IgPSAiYmxhY2siKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgU3RhcnR1cCBTdGF0dXMiLCB4ID0gIlN0YXR1cyIsIHkgPSAiQ291bnQiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgojIFByaW50IHRoZSBwbG90CnByaW50KGdnKQoKIyBBZGQgYW4gZXh0ZXJuYWwgYW5ub3RhdGlvbiB0byB0aGUgcmlnaHQgc2lkZQpncmlkLnRleHQoIjEgPSBBY3F1aXJlZFxuMCA9IENsb3NlZCIsIHggPSAwLjksIHkgPSAwLjkyLCBqdXN0ID0gYygicmlnaHQiLCAidG9wIiksIGdwID0gZ3Bhcihmb250c2l6ZSA9IDEyLCBjb2wgPSAiYmxhY2siKSkKYGBgCgpBcyBzaG93biBhYm92ZSwgKip0aGVyZSBpcyBhIGNsYXNzIGltYmFsYW5jZSBpbiB0aGUgY2xhc3MgbGFiZWwgKHN0YXR1cykqKi4gSW4gdGhlIGRhdGFzZXQsIG9iamVjdHMgd2l0aCBhY3F1aXJlZCBzdGF0dXMgYXJlIGFsbW9zdCB0d2ljZSBhcyBtdWNoIGFzIG9iamVjdHMgd2l0aCBjbG9zZWQgb2JqZWN0cy4gVGhpcyBpbmRpY2F0ZXMgdGhhdCB0aGUgY2xhc3MgImFjcXVpcmVkIiBpcyBtb3JlIHByZXZhbGVudCB0aGFuIHRoZSBjbGFzcyAiY2xvc2VkIiBpbiB0aGUgZGF0YXNldC4gSW4gbGF0ZXIgc3RlcHMsIHRoaXMgbWF5IHByZXNlbnQgYSBjaGFsbGVuZ2UgaW4gdHJhaW5pbmcgYW5kIHRlc3RpbmcgdGhlIG1vZGVsLiA8YnI+PGJyPgoKIyMjIFNhbXBsZSBvZiBSYXcgRGF0YXNldCBhbmQgU3VtbWFyeToKCkhlcmUsIHdlIGFpbSB0byBleHBsb3JlIHRoZSBkYXRhIG9mIG91ciBkYXRhc2V0LiBGaXZlLW51bWJlciBzdW1tYXJ5LCB2YXJpYW5jZSwgbWlzc2luZyB2YWx1ZXMsIGFuZCBncmFwaHMgYXJlIHV0aWxpemVkIHRvIGdpdmUgaW5zaWdodCBpbnRvIHRoZSBkYXRhIHdlIGFyZSBkZWFsaW5nIHdpdGguIE5vbWluYWwsIG51bWVyaWNhbCwgYW5kIGJpbmFyeSBkYXRhIHdpbGwgYmUgZXhhbWluZWQuPGJyPgoKYGBge3J9CnN0cihkYXRhc2V0KQpgYGAKQWJvdmUgaXMgYSBzbmFwc2hvdCBvZiB0aGUgcmF3IGRhdGFzZXQuIFRoZSBzdGFydHVwIGRhdGEgaXMgYSBjb2xsZWN0aW9uIG9mIGRpZmZlcmVudCBkYXRhIHR5cGVzLjxicj48YnI+CgojIyMjIEEpIE51bWVyaWNhbCBhdHRyaWJ1dGVzOgoKIyMjIyMgaSkgRml2ZS1udW1iZXIgc3VtbWFyeToKCkJlbG93LCBhIGZpdmUtbnVtYmVyIHN1bW1hcnkgaXMgcGVyZm9ybWVkIG9uIG51bWVyaWNhbCBhdHRyaWJ1dGVzIHRvIGdhaW4gaW5zaWdodHMgaW50byB0aGUgZGlzdHJpYnV0aW9uIGFuZCBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIG51bWVyaWMgZGF0YS4gVGhlIGZpdmUgbnVtYmVyIHN1bW1hcnkgaXMgcmVwcmVzZW50ZWQgYnkgdGhlIG1pbmltdW0sIGZpcnN0IHF1YXJ0aWxlIChRMSksIG1lZGlhbiAoUTIpLCB0aGlyZCBxdWFydGlsZSAoUTMpLCBhbmQgbWF4aW11bSBvZiB0aGUgYXZhaWxhYmxlIG51bWVyaWMgYXR0cmlidXRlcy4gCgpgYGB7cn0KIyBTcGVjaWZ5IG51bWVyaWNhbCBhdHRyaWJ1dGVzIGZvciB0aGUgZml2ZS1udW1iZXIgc3VtbWFyeQpudW1lcmljYWxfYXR0cmlidXRlcyA8LSBjKCJsYXRpdHVkZSIsICJsb25naXR1ZGUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiYWdlX2ZpcnN0X2Z1bmRpbmdfeWVhciIsICJhZ2VfbGFzdF9mdW5kaW5nX3llYXIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyIiwgImFnZV9sYXN0X21pbGVzdG9uZV95ZWFyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcHMiLCAiZnVuZGluZ19yb3VuZHMiLCAiZnVuZGluZ190b3RhbF91c2QiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAibWlsZXN0b25lcyIsICJhdmdfcGFydGljaXBhbnRzIikKCiMgU3Vic2V0IHRoZSBkYXRhIHRvIGluY2x1ZGUgb25seSBudW1lcmljYWwgYXR0cmlidXRlcwpudW1lcmljYWxfZGF0YSA8LSBkYXRhc2V0WywgbnVtZXJpY2FsX2F0dHJpYnV0ZXNdCgojIENhbGN1bGF0ZSB0aGUgZml2ZS1udW1iZXIgc3VtbWFyeQpzdW1tYXJ5X2RhdGEgPC0gc3VtbWFyeShudW1lcmljYWxfZGF0YSkKcHJpbnQoc3VtbWFyeV9kYXRhKQpgYGAKQXMgZGVtb25zdHJhdGVkIGFib3ZlIGJ5IHRoZSBmaXZlLW51bWJlciBzdW1tYXJ5LCBzb21lIGF0dHJpYnV0ZXMgY29udGFpbiBuZWdhdGl2ZSB2YWx1ZXMuIE5lZ2F0aXZlIHZhbHVlcyBpbiBhdHRyaWJ1dGVzIHJlbGF0ZWQgdG8gYWdlIGFuZCBsb25naXR1ZGUgYXJlIGlsbG9naWNhbCBhbmQgbGlrZWx5IGVycm9ycy4gV2Ugd2lsbCBhZGRyZXNzIHRoaXMgaXNzdWUgYnkgcmVtb3ZpbmcgdGhlc2UgbmVnYXRpdmUgdmFsdWVzIGR1cmluZyB0aGUgZGF0YSBjbGVhbmluZyBwcm9jZXNzLiA8YnI+CgojIyMjIyBpaSkgVmFyaWFuY2U6CgpgYGB7cn0KIyBMaXN0IG9mIG51bWVyaWNhbCBhdHRyaWJ1dGVzCm51bWVyaWNhbF9hdHRyaWJ1dGVzIDwtIGMoImxhdGl0dWRlIiwgImxvbmdpdHVkZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgImFnZV9maXJzdF9mdW5kaW5nX3llYXIiLCAiYWdlX2xhc3RfZnVuZGluZ195ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyIiwgImFnZV9sYXN0X21pbGVzdG9uZV95ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAicmVsYXRpb25zaGlwcyIsICJmdW5kaW5nX3JvdW5kcyIsICJmdW5kaW5nX3RvdGFsX3VzZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIm1pbGVzdG9uZXMiLCAiYXZnX3BhcnRpY2lwYW50cyIpCgojIENhbGN1bGF0ZSB2YXJpYW5jZXMgZm9yIG51bWVyaWNhbCBhdHRyaWJ1dGVzCnZhcmlhbmNlc19udW1lcmljYWwgPC0gc2FwcGx5KGRhdGFzZXRbLCBudW1lcmljYWxfYXR0cmlidXRlc10sIHZhcikKCiMgUHJpbnQgdmFyaWFuY2VzIGZvciBudW1lcmljYWwgYXR0cmlidXRlcwpmb3IgKGkgaW4gc2VxX2Fsb25nKG51bWVyaWNhbF9hdHRyaWJ1dGVzKSkgewogIGNhdCgiVmFyaWFuY2UgZm9yIiwgcGFzdGUobnVtZXJpY2FsX2F0dHJpYnV0ZXNbaV0sICI6IiwgdmFyaWFuY2VzX251bWVyaWNhbFtpXSksICJcbiIpCn0KYGBgCioqVGhlIHZhcmlhbmNlIGNhbiBiZSBjbGFzc2lmaWVkIGludG8gZm91ciBjbGFzc2VzOiBsb3csIG1vZGVyYXRlLCBoaWdoLCBhbmQgdmVyeSBoaWdoLioqIApBdHRyaWJ1dGVzIGFnZV9maXJzdF9taWxlc3RvbmVfeWVhciBhbmQgYWdlX2xhc3RfbWlsZXN0b25lX3llYXIgaW5jbHVkZSBtaXNzaW5nIHZhbHVlcyB3aGljaCBwcmV2ZW50cyBhbiBhY2N1cmF0ZSBhc3Nlc3NtZW50IG9mIHZhcmlhYmlsaXR5LiAKCkF0dHJpYnV0ZXMgbG9uZ2l0dWRlLCBhbmQgZnVuZGluZ190b3RhbF91c2QgYXR0cmlidXRlIGRlbW9uc3RyYXRlcyBhbiAqKmV4Y2VwdGlvbmFsbHkgaGlnaCBsZXZlbCBvZiB2YXJpYWJpbGl0eSoqLCBsaWtlbHkgaW5mbHVlbmNlZCBieSBvdXRsaWVycyBvciBhIGJyb2FkIHJhbmdlIG9mIGZ1bmRpbmcgdmFsdWVzLgoKVGhlIHJlbGF0aW9uc2hpcHMgYXR0cmlidXRlIHNob3dzIGEgKipoaWdoIGxldmVsIG9mIHZhcmlhYmlsaXR5KiosIGluZGljYXRpbmcgYSB3aWRlIHJhbmdlIG9mIHZhbHVlcyBhbW9uZyBzdGFydHVwcy4KCkF0dHJpYnV0ZXMgbGF0aXR1ZGUgYW5kIGFnZV9sYXN0X2Z1bmRpbmdfeWVhciBkaXNwbGF5IGEgKiptb2RlcmF0ZSBsZXZlbCBvZiB2YXJpYW5jZSoqIHdpdGggcmVzcGVjdCB0byB0aGUgZGF0YXNldC4gCgpBdHRyaWJ1dGVzIG9mICoqbG93IHZhcmlhbmNlKiogYXJlIGFnZV9maXJzdF9mdW5kaW5nX3llYXIsIGZ1bmRpbmdfcm91bmRzLCBhbmQgbWlsZXN0b25lcywgd2hpY2ggbWlnaHQgYmUgZHVlIHRvIHRoZWlyIG1pbmltYWwgcmFuZ2UgcmVzdWx0aW5nIGluIGEgbW9yZSBjb25jZW50cmF0ZWQgZGlzdHJpYnV0aW9uIG9mIHZhbHVlcy4gV2hpY2ggYnJpbmdzIHVzIHRvIHRoZSB0aGlyZCBudW1lcmljYWwgZGF0YSBhc3Nlc3NtZW50IG1ldHJpYzogPGJyPgoKCiMjIyMjIGlpaSkgTWlzaW5nIHZhbHVlczoKYGBge3J9CiMgTGlzdCBvZiBudW1lcmljYWwgYXR0cmlidXRlcwpudW1lcmljYWxfYXR0cmlidXRlcyA8LSBjKCJsYXRpdHVkZSIsICJsb25naXR1ZGUiLCAiemlwX2NvZGUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiYWdlX2ZpcnN0X2Z1bmRpbmdfeWVhciIsICJhZ2VfbGFzdF9mdW5kaW5nX3llYXIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyIiwgImFnZV9sYXN0X21pbGVzdG9uZV95ZWFyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcHMiLCAiZnVuZGluZ19yb3VuZHMiLCAiZnVuZGluZ190b3RhbF91c2QiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAibWlsZXN0b25lcyIsICJhdmdfcGFydGljaXBhbnRzIikKCm1pc3NpbmdfbnVtZXJpY2FsX3ZhbHVlcyA8LSBzYXBwbHkoZGF0YXNldFssIG51bWVyaWNhbF9hdHRyaWJ1dGVzXSwgZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKdG90YWxfbnVtZXJpY2FsX3ZhbHVlcyA8LSBzdW0obWlzc2luZ19udW1lcmljYWxfdmFsdWVzKQoKcHJpbnQodG90YWxfbnVtZXJpY2FsX3ZhbHVlcykKYGBgClRoZXJlIGlzIGEgdG90YWwgb2YgMzA0IG1pc3NpbmcgdmFsdWVzIGluIHRoZSBjb2x1bW5zIG9mIG51bWVyaWNhbCBhdHRyaWJ1dGVzLiBXZSdsbCBhZGRyZXNzIHRoaXMgaXNzdWUgYnkgcmVtb3ZpbmcgdGhlc2UgbWlzc2luZyB2YWx1ZXMgZHVyaW5nIHRoZSBkYXRhIGNsZWFuaW5nIHByb2Nlc3MuIDxicj48YnI+CgojIyMjIEIpIEJpbmFyeSBhbmQgTm9taW5hbCBBdHRyaWJ1dGVzOgoKIyMjIyMgaSkgRml2ZS1udW1iZXIgc3VtbWFyeToKClNpbmNlIHdlIGFyZSBhZGRyZXNzaW5nIGJpbmFyeSBhbmQgbm9taW5hbCBkYXRhLCB0aGUgZml2ZS1udW1iZXIgc3VtbWFyeSB3b24ndCBnaXZlIHVzIHVzZWZ1bCBpbnNpZ2h0cy4gVGhlcmVmb3JlLCB3ZSB3b24ndCBkbyB0aGUgZml2ZS1udW1iZXIgc3VtbWFyeSBmb3IgdGhvc2UgdHlwZXMgb2YgYXR0cmlidXRlcy4gPGJyPgoKIyMjIyMgaWkpIFZhcmlhbmNlOgoKVmFyaWFuY2UgY2Fubm90IGJlIHBlcmZvcm1lZCBvbiBub21pbmFsIGF0dHJpYnV0ZXMsIHNvIHdlIHdpbGwgb25seSBjb25kdWN0IGl0IG9uIGJpbmFyeSBhdHRyaWJ1dGVzLiAKCmBgYHtyfQojIExpc3Qgb2YgYmluYXJ5IGF0dHJpYnV0ZXMKYmluYXJ5X2F0dHJpYnV0ZXMgPC0gYygiaXNfQ0EiLCAiaXNfTlkiLCAiaXNfTUEiLCAiaXNfVFgiLCAiaXNfb3RoZXJzdGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJpc19zb2Z0d2FyZSIsICJpc193ZWIiLCAiaXNfbW9iaWxlIiwgImlzX2VudGVycHJpc2UiLAogICAgICAgICAgICAgICAgICAgICAgICAiaXNfYWR2ZXJ0aXNpbmciLCAiaXNfZ2FtZXN2aWRlbyIsICJpc19lY29tbWVyY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAiaXNfYmlvdGVjaCIsICJpc19jb25zdWx0aW5nIiwgImlzX290aGVyY2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAiaGFzX1ZDIiwgImhhc19hbmdlbCIsICJoYXNfcm91bmRBIiwgImhhc19yb3VuZEIiLAogICAgICAgICAgICAgICAgICAgICAgICAiaGFzX3JvdW5kQyIsICJoYXNfcm91bmREIiwgImxhYmVscyIsICJpc190b3A1MDAiLCAic3RhdHVzIikKCiMgQ2FsY3VsYXRlIHZhcmlhbmNlcyBmb3IgYmluYXJ5IGF0dHJpYnV0ZXMKdmFyaWFuY2VzX2JpbmFyeSA8LSBzYXBwbHkoZGF0YXNldFssIGJpbmFyeV9hdHRyaWJ1dGVzXSwgdmFyKQoKIyBQcmludCB2YXJpYW5jZXMgZm9yIGJpbmFyeSBhdHRyaWJ1dGVzCmZvciAoaSBpbiBzZXFfYWxvbmcoYmluYXJ5X2F0dHJpYnV0ZXMpKSB7CiAgY2F0KCJWYXJpYW5jZSBmb3IiLCBwYXN0ZShiaW5hcnlfYXR0cmlidXRlc1tpXSwgIjoiLCB2YXJpYW5jZXNfYmluYXJ5W2ldKSwgIlxuIikKfQpgYGAKVGhlc2UgdmFsdWVzIGluZGljYXRlIGhvdyBtdWNoIHRoZSB2YWx1ZXMgaW4gZWFjaCBiaW5hcnkgYXR0cmlidXRlIGRldmlhdGUgZnJvbSB0aGVpciBtZWFuICgwLjUgZm9yIGJhbGFuY2VkIGJpbmFyeSB2YXJpYWJsZXMpLiBMb3dlciB2YXJpYW5jZXMgc3VnZ2VzdCB0aGF0IHRoZSB2YWx1ZXMgYXJlIGNsb3NlciB0byB0aGUgbWVhbiwgd2hpbGUgaGlnaGVyIHZhcmlhbmNlcyBpbmRpY2F0ZSBncmVhdGVyIHNwcmVhZC4gU2luY2UgdGhlIHZhcmlhbmNlIGZvciBhbGwgdGhlc2UgYmluYXJ5IGF0dHJpYnV0ZXMgaXMgdmVyeSBsb3csIHRoZSB2YXJpYW5jZSBpbmRpY2F0ZXMgbm8gZnVydGhlciBzaWduaWZpY2FuY2UuIAoKQWx0aG91Z2ggc3RhdHVzIGlzIGEgYmluYXJ5IGF0dHJpYnV0ZSwgaXQgaGFzbid0IGJlZW4gZW5jb2RlZCB0byAxIChhY3F1aXJlZCkgYW5kIDAgKGNsb3NlZCkgeWV0LiBTbywgaXRzIHZhcmlhbmNlIGlzIGRlbm90ZWQgYnkgTkEuIEFzIGl0IHNlZW1zIGEgbm9taW5hbCBhdHRyaWJ1dGUuIDxicj4KCiMjIyMjIGlpaSkgTWlzaW5nIHZhbHVlczoKCmBgYHtyfQojIExpc3Qgb2YgYXR0cmlidXRlcwpiaW5hcnlfbm9taW5hbF9hdHRyaWJ1dGVzIDwtIGMoIlVubmFtZWQ6IDAiLCAic3RhdGVfY29kZSIsICJpZCIsICJ6aXBfY29kZSIsICJjaXR5IiwgIlVubmFtZWQ6IDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiwgImZvdW5kZWRfYXQiLCAiY2xvc2VkX2F0IiwgImZpcnN0X2Z1bmRpbmdfYXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJsYXN0X2Z1bmRpbmdfYXQiLCAic3RhdGVfY29kZS4xIiwgImNhdGVnb3J5X2NvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJvYmplY3RfaWQiLCAiaXNfQ0EiLCAiaXNfTlkiLCAiaXNfTUEiLCAiaXNfVFgiLCAiaXNfb3RoZXJzdGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgImlzX3NvZnR3YXJlIiwgImlzX3dlYiIsICJpc19tb2JpbGUiLCAiaXNfZW50ZXJwcmlzZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgImlzX2FkdmVydGlzaW5nIiwgImlzX2dhbWVzdmlkZW8iLCAiaXNfZWNvbW1lcmNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiaXNfYmlvdGVjaCIsICJpc19jb25zdWx0aW5nIiwgImlzX290aGVyY2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJoYXNfVkMiLCAiaGFzX2FuZ2VsIiwgImhhc19yb3VuZEEiLCAiaGFzX3JvdW5kQiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgImhhc19yb3VuZEMiLCAiaGFzX3JvdW5kRCIsICJsYWJlbHMiLCAiaXNfdG9wNTAwIiwgInN0YXR1cyIpCgptaXNzaW5nX2JpbmFyeV9ub21pbmFsX3ZhbHVlcyA8LSBzYXBwbHkoZGF0YXNldFssIGJpbmFyeV9ub21pbmFsX2F0dHJpYnV0ZXNdLCBmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpKQp0b3RhbF9iaW5hcnlfbm9taW5hbF92YWx1ZXMgPC0gc3VtKG1pc3NpbmdfYmluYXJ5X25vbWluYWxfdmFsdWVzKQoKcHJpbnQodG90YWxfYmluYXJ5X25vbWluYWxfdmFsdWVzKQpgYGAKVGhlcmUgaXMgYSB0b3RhbCBvZiAxMDgyIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBjb2x1bW5zIG9mIGJpbmFyeSBhbmQgbm9taW5hbCBhdHRyaWJ1dGVzLiBXZSdsbCBhZGRyZXNzIHRoaXMgaXNzdWUgYnkgcmVtb3ZpbmcgdGhlc2UgbWlzc2luZyB2YWx1ZXMgZHVyaW5nIHRoZSBkYXRhIGNsZWFuaW5nIHByb2Nlc3MuIDxicj4KCgojIyMjIyBEZXRlY3RpbmcgYW5kIERlYWxpbmcgV2l0aCBNaXNzaW5nIFZhbHVlcyAKCldlIHdpbGwgZGV0ZWN0IGFuZCBlbGltaW5hdGUgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQgdG8gZW5zdXJlIHRoZSBjcmVhdGlvbiBvZiByZXByZXNlbnRhdGl2ZSBncmFwaHMsIGFzIHBsb3R0aW5nIHJlcXVpcmVzIGFkZHJlc3NpbmcgbWlzc2luZyB2YWx1ZXMgZmlyc3QuCgpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQpKQpgYGAKVGhlcmUgaXMgYSB0b3RhbCBvZiAxMzg2IG1pc3NpbmcgdmFsdWVzIGluIHRoZSBzdGFydHVwIGRhdGFzZXQuIAoKYGBge3J9CiMgQ2FsY3VsYXRlIHRoZSBzdW0gb2YgbWlzc2luZyB2YWx1ZXMgZm9yIGVhY2ggYXR0cmlidXRlCnN1bV9taXNzaW5nX3ZhbHVlcyA8LSBmdW5jdGlvbihhdHRyaWJ1dGUpIHsKICBzdW0oaXMubmEoYXR0cmlidXRlKSkKfQoKIyBBcHBseSB0aGUgZnVuY3Rpb24gdG8gZWFjaCBhdHRyaWJ1dGUgYW5kIHN0b3JlIHRoZSByZXN1bHRzIGluIGEgZGF0YSBmcmFtZQptaXNzaW5nX3ZhbHVlcyA8LSBkYXRhLmZyYW1lKAogIEF0dHJpYnV0ZSA9IG5hbWVzKGRhdGFzZXQpLAogIE1pc3NpbmdfVmFsdWVzID0gc2FwcGx5KGRhdGFzZXQsIHN1bV9taXNzaW5nX3ZhbHVlcykKKQoKIyBQcmludCB0aGUgcmVzdWx0CnByaW50KG1pc3NpbmdfdmFsdWVzKQpgYGAKClRoZSB0YWJsZSBhYm92ZSBzaG93cyBlYWNoIGF0dHJpYnV0ZSB3aXRoIHRoZSBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgZm91bmQgaW4gaXRzIGNvbHVtbi4gQWxsIG1pc3NpbmcgdmFsdWVzIGNvbWUgZnJvbSBmaXZlIGF0dHJpYnV0ZXMsIG9yZGVyZWQgZnJvbSBtb3N0IG1pc3NpbmcgdG8gbGVhc3Q6IGNsb3NlZF9hdCwgVW5uYW1lZDogNiwgYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyLCAJYWdlX2xhc3RfbWlsZXN0b25lX3llYXIsIGFuZCAJc3RhdGVfY29kZS4xLiAKCiMjIyMgUmVwbGFjaW5nIE1pc3NpbmcgVmFsdWVzCgpGb3IgYXR0cmlidXRlIGNsb3NlZF9hdCBhbmQgc3RhdGVfY29kZS4xLCB3ZSB3aWxsIGZpbGwgYWxsIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIGdsb2JhbCBjb25zdGFudCBOL0EgYmVjYXVzZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgb3BlcmF0aW9uIG9mIHRoZSBjb21wYW55ICh3aGV0aGVyIGlzIGlzIHN0aWxsIG9wZW4gb3IgY2xvc2VkKSByZW1haW5zIHVua25vd24gYW5kIGNhbm5vdCBiZSByZXBsYWNlZCB3aXRoIHRoZSBhdmVyYWdlIGFzIHRoZSBjb21wYW55IG1heSBzdGlsbCBiZSBvcGVyYXRpbmcuIAoKTm90ZTogc3RhdGVfY29kZS4xIGlzIGEgZHVwbGljYXRlIGF0dHJpYnV0ZS4gVW5uYW1lZDogNiBpcyBhbiBpcnJlbGV2YW50IGF0dHJpYnV0ZSBhcyBleHBsYWluZWQgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGlzIHNlY3Rpb24uIEJvdGggc3RhdGVfY29kZS4xIGFuZCBVbm5hbWVkOiA2IHdpbGwgYmUgcmVtb3ZlZCBpbiB0aGUgZGF0YSByZWR1Y3Rpb24gc3RlcC4gCgojIyMjIEJlZm9yZSByZXBsYWNpbmcgbWlzc2luZyB2YWx1ZXMgb2YgYXR0cmlidXRlIGNsb3NlZF9hdCB3aXRoIE4vQToKYGBge3J9CmhlYWQoZGF0YXNldCRjbG9zZWRfYXQpCmBgYApBcyBzaG93biBpbiB0aGUgdGFibGUgYWJvdmUsIG1pc3NpbmcgdmFsdWVzIGFyZSBhdXRvbWF0aWNhbGx5IGRlbm90ZWQgYXMgIk5BIiBpbiBSLiAKCiMjIyMgUmVwbGFjaW5nIHdpdGggIk4vQSI6CmBgYHtyfQpkYXRhc2V0JGNsb3NlZF9hdFtpcy5uYShkYXRhc2V0JGNsb3NlZF9hdCldIDwtICJOL0EiCmRhdGFzZXQkc3RhdGVfY29kZS4xW2lzLm5hKGRhdGFzZXQkc3RhdGVfY29kZS4xKV0gPC0gIk4vQSIKZGF0YXNldCQnVW5uYW1lZDogNidbaXMubmEoZGF0YXNldCQnVW5uYW1lZDogNicpXSA8LSAiTi9BIgpgYGAKVGhpcyBjb2RlIGNodW5rIHJlcGxhY2VzIGFsbCBtaXNzaW5nIHZhbHVlcyBpbiBhdHRyaWJ1dGUgY2xvc2VkX2F0IHdpdGggTi9BLgoKIyMjIyBBZnRlciByZXBsYWNpbmcgbWlzc2luZyB2YWx1ZXMgb2YgYXR0cmlidXRlIGNsb3NlZF9hdCB3aXRoIE4vQToKYGBge3J9CmhlYWQoZGF0YXNldCRjbG9zZWRfYXQpCmBgYApBcyBzaG93biBpbiB0aGUgdGFibGUgYWJvdmUsIG1pc3NpbmcgdmFsdWVzIGFyZSByZXBsYWNlZCB3aXRoIGdsb2JhbCBjb25zdGFudCAiTi9BIi4gCgoKRm9yIGF0dHJpYnV0ZXMgYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyIGFuZCBhZ2VfbGFzdF9taWxlc3RvbmVfeWVhciB3ZSB3aWxsIHJlcGxhY2UgbWlzc2luZyB2YWx1ZXMgd2l0aCB0aGUgYXR0cmlidXRlJ3MgbWVhbi4gPGJyPgoKIyMjIyBCZWZvcmUgcmVwbGFjaW5nIG1pc3NpbmcgdmFsdWVzIG9mIGF0dHJpYnV0ZXMgYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyIGFuZCBhZ2VfbGFzdF9taWxlc3RvbmVfeWVhciB3aXRoIGF2ZXJhZ2U6CmBgYHtyfQpyb3dfMTMgPC0gZGF0YXNldFsxMywgYygiYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyIiwgImFnZV9sYXN0X21pbGVzdG9uZV95ZWFyIildCgojIERpc3BsYXkgdGhlIHJlc3VsdApwcmludChyb3dfMTMpCmBgYApBcyBzaG93biBpbiByb3cgMTMgb2YgdGhlIGRhdGFzZXQsIG1pc3NpbmcgdmFsdWVzIGFyZSBhdXRvbWF0aWNhbGx5IGRlbm90ZWQgYXMgIk5BIiBpbiBSLiAKCiMjIyMgUmVwbGFjaW5nIHdpdGggYXZlcmFnZToKYGBge3J9CmRhdGFzZXQkYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyID0gaWZlbHNlIChpcy5uYShkYXRhc2V0JGFnZV9maXJzdF9taWxlc3RvbmVfeWVhciksIGF2ZShkYXRhc2V0JGFnZV9maXJzdF9taWxlc3RvbmVfeWVhciwgRlVOPWZ1bmN0aW9uKHgpbWVhbih4LG5hLnJtPVRSVUUpKSwgZGF0YXNldCRhZ2VfZmlyc3RfbWlsZXN0b25lX3llYXIpCgpkYXRhc2V0JGFnZV9sYXN0X21pbGVzdG9uZV95ZWFyID0gaWZlbHNlIChpcy5uYShkYXRhc2V0JGFnZV9sYXN0X21pbGVzdG9uZV95ZWFyKSwgYXZlKGRhdGFzZXQkYWdlX2xhc3RfbWlsZXN0b25lX3llYXIsIEZVTj1mdW5jdGlvbih4KW1lYW4oeCxuYS5ybT1UUlVFKSksIGRhdGFzZXQkYWdlX2xhc3RfbWlsZXN0b25lX3llYXIpCmBgYApUaGlzIGNvZGUgY2h1bmsgcmVwbGFjZXMgYWxsIG1pc3NpbmcgdmFsdWVzIG9mIGF0dHJpYnV0ZXMgYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyIGFuZCBhZ2VfbGFzdF9taWxlc3RvbmVfeWVhciB3aXRoIHRoZWlyIGF2ZXJhZ2UuCgojIyMjIEFmdGVyIHJlcGxhY2luZyBtaXNzaW5nIHZhbHVlcyBvZiBhdHRyaWJ1dGVzIGFnZV9maXJzdF9taWxlc3RvbmVfeWVhciBhbmQgYWdlX2xhc3RfbWlsZXN0b25lX3llYXIgd2l0aCBhdmVyYWdlOgpgYGB7cn0Kcm93XzEzIDwtIGRhdGFzZXRbMTMsIGMoImFnZV9maXJzdF9taWxlc3RvbmVfeWVhciIsICJhZ2VfbGFzdF9taWxlc3RvbmVfeWVhciIpXQoKIyBEaXNwbGF5IHRoZSByZXN1bHQKcHJpbnQocm93XzEzKQpgYGAKQXMgc2hvd24gaW4gcm93IDEzIG9mIHRoZSBkYXRhc2V0LCBtaXNzaW5nIHZhbHVlcyBhcmUgcmVwbGFjZWQgd2l0aCB0aGUgYXR0cmlidXRlIGF2ZXJhZ2UuIAoKIyMjIyBDaGVjayBmb3IgcmVtYWluaW5nIG1pc3NpbmcgdmFsdWVzOgpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQpKQpgYGAKQWxsIG1pc3NpbmcgdmFsdWVzIGFyZSBhZGRyZXNzZWQuIFRoZXJlIGFyZSBubyByZW1haW5pbmcgbWlzc2luZyB2YWx1ZXMuIAoKCiMjIyBHcmFwaGljYWwgUmVwcmVzZW50YXRpb24gb2YgdGhlIERhdGFzZXQgKCBCZWZvcmUgKSBEYXRhIFByZS1Qcm9jZXNzaW5nIAo8YnI+CgojIyMjIEEpIE51bWVyaWNhbCBBdHRyaWJ1dGVzIEhpc3RvZ3JhbToKYGBge3J9CgpgYGAKCmBgYHtyfQojIExvYWQgdGhlIHJlcXVpcmVkIGxpYnJhcmllcwpsaWJyYXJ5KGdncGxvdDIpCgojIFNlbGVjdCBudW1lcmljYWwgYXR0cmlidXRlcyBmb3IgaGlzdG9ncmFtcwpudW1lcmljYWxfYXR0cmlidXRlcyA8LSBjKCJsYXRpdHVkZSIsICJsb25naXR1ZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiYWdlX2ZpcnN0X2Z1bmRpbmdfeWVhciIsICJhZ2VfbGFzdF9mdW5kaW5nX3llYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyIiwgImFnZV9sYXN0X21pbGVzdG9uZV95ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcHMiLCAiZnVuZGluZ19yb3VuZHMiLCAiZnVuZGluZ190b3RhbF91c2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibWlsZXN0b25lcyIsICJhdmdfcGFydGljaXBhbnRzIikKCiMgTWVsdCB0aGUgZGF0YXNldCBmb3IgZWFzaWVyIHBsb3R0aW5nCm1lbHRlZF9kYXRhIDwtIHJlc2hhcGUyOjptZWx0KGRhdGFzZXRbLCBudW1lcmljYWxfYXR0cmlidXRlc10pCgojIENyZWF0ZSBoaXN0b2dyYW0gd2l0aCBmYWNldCB3cmFwCmhpc3RvZ3JhbV9wbG90X251bWVyaWNhbF9iZWZvcmUgPC0gZ2dwbG90KG1lbHRlZF9kYXRhLCBhZXMoeCA9IHZhbHVlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSwgZmlsbCA9ICJkYXJrZ3JheSIsIGNvbG9yID0gImJsYWNrIikgKwogIGZhY2V0X3dyYXAofnZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZSIpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBmb3IgTnVtZXJpY2FsIEF0dHJpYnV0ZXMgQkVGT1JFIFByZS1wcm9jZXNzaW5nIiwgeCA9ICJWYWx1ZSIsIHkgPSAiRnJlcXVlbmN5IikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKIyBEaXNwbGF5IHRoZSBwbG90CnBhcihtZnJvdyA9IGMoMSwgMikpICAjIFNldCB1cCBhIDF4MiBwbG90dGluZyBncmlkCnBsb3QoaGlzdG9ncmFtX3Bsb3RfbnVtZXJpY2FsX2JlZm9yZSkgICMgUGxvdCB0aGUgb3JpZ2luYWwgZ3JhcGgKYGBgCkluIHRoZSBjb2xsZWN0aW9uIG9mIGhpc3RvZ3JhbXMgYWJvdmUsIHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBpcyBhIG5vdGljZWFibGUgZGlzdHJpYnV0aW9uIGFtb25nIHZhbHVlcyBvZiBjZXJ0YWluIGF0dHJpYnV0ZXMuIEFzIHNob3duLCAiZnVuZGluZ190b3RhbF91c2QiIGlzIG5vdCBhcHBlYXJpbmcgYXMgZXhwZWN0ZWQsIGl0IGNvdWxkIGJlIGR1ZSB0byB0aGUgcHJlc2VuY2Ugb2YgZXh0cmVtZSB2YWx1ZXMgb3Igb3V0bGllcnMgdGhhdCBhcmUgYWZmZWN0aW5nIHRoZSB2aXN1YWxpemF0aW9uLiBUaGUgZ3JhcGhzIGFib3ZlIGluZGljYXRlIHRoYXQgd2UgbXVzdCByZW1vdmUgb3V0bGllcnMgYW5kIGRlYWwgd2l0aCBuZWdhdGl2ZSB2YWx1ZXMuIAoKYGBge3J9CiMgQXNzdW1pbmcgeW91ciBkYXRhc2V0IGlzIG5hbWVkICd5b3VyX2RhdGFzZXQnCiMgUmVwbGFjZSAneW91cl9kYXRhc2V0JyB3aXRoIHRoZSBhY3R1YWwgbmFtZSBvZiB5b3VyIGRhdGFzZXQKCiMgU2VsZWN0IG51bWVyaWNhbCBhdHRyaWJ1dGVzIGZvciBoaXN0b2dyYW1zCm51bWVyaWNhbF9hdHRyaWJ1dGVzIDwtIGMoImxhdGl0dWRlIiwgImxvbmdpdHVkZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhZ2VfZmlyc3RfZnVuZGluZ195ZWFyIiwgImFnZV9sYXN0X2Z1bmRpbmdfeWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhZ2VfZmlyc3RfbWlsZXN0b25lX3llYXIiLCAiYWdlX2xhc3RfbWlsZXN0b25lX3llYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicmVsYXRpb25zaGlwcyIsICJmdW5kaW5nX3JvdW5kcyIsICJmdW5kaW5nX3RvdGFsX3VzZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJtaWxlc3RvbmVzIiwgImF2Z19wYXJ0aWNpcGFudHMiKQoKIyBNZWx0IHRoZSBkYXRhc2V0IGZvciBlYXNpZXIgcGxvdHRpbmcKbWVsdGVkX2RhdGEgPC0gcmVzaGFwZTI6Om1lbHQoZGF0YXNldFssIG51bWVyaWNhbF9hdHRyaWJ1dGVzXSkKCiMgQ3JlYXRlIGhpc3RvZ3JhbSB3aXRoIGZhY2V0IHdyYXAsIGxvZy10cmFuc2Zvcm1pbmcgZnVuZGluZ190b3RhbF91c2QKaGlzdG9ncmFtX3Bsb3QgPC0gZ2dwbG90KG1lbHRlZF9kYXRhLCBhZXMoeCA9IGxvZyh2YWx1ZSArIDEpKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4yLCBmaWxsID0gImRhcmtncmF5IiwgY29sb3IgPSAiYmxhY2siKSArCiAgZmFjZXRfd3JhcCh+dmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIikgKwogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIGZvciBOdW1lcmljYWwgQXR0cmlidXRlcyBCRUZPUkUgUHJlLXByb2Nlc3NpbmciLAogICAgICAgeCA9ICJMb2coVmFsdWUgKyAxKSIsCiAgICAgICB5ID0gIkZyZXF1ZW5jeSIpKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKIyBEaXNwbGF5IHRoZSBwbG90CnByaW50KGhpc3RvZ3JhbV9wbG90KQpgYGAKSGVyZSwgd2UgZml4ZWQgdGhlIGlzc3VlIHJlZ2FyZGluZyB0aGUgImZ1bmRpbmdfdG90YWxfdXNkIiBncmFwaC4gV2UgdXNlZCB0aGUgbG9nKHZhbHVlICsgMSkgdG8gbG9nLXRyYW5zZm9ybSB0aGUgImZ1bmRpbmdfdG90YWxfdXNkIiB2YWx1ZXMuIEFsdGhvdWdoIHRoZSAiZnVuZGluZ190b3RhbF91c2QiIGdyYXBoIGFwcGVhcmVkLCB3ZSBoYXZlIGVsaW1pbmF0ZWQgbmVnYXRpdmUgdmFsdWVzLiBUaGlzIGluZGljYXRlcyB0aGF0IHRoZSBncmFwaGljYWwgcmVwcmVzZW50YXRpb24gYWJvdmUgaXMgbm90IGFuIGFjY3VyYXRlIHJlcHJlc2VudGF0aW9uIG9mIHRoZSByYXcgbnVtZXJpY2FsIGF0dHJpYnV0ZXMgYnV0IGFuIGVzdGltYXRlIG9mIG9uZS4gPGJyPgoKIyMjIyBCKSBOb21pbmFsIEF0dHJpYnV0ZXMgQmFycGxvdDoKCkF0dHJpYnV0ZXMgVW5uYW1lZDogMCwgaWQsIHppcF9jb2RlLCBVbm5hbWVkOiA2LCBvYmplY3RfaWQgYXJlIHVzZWQgZm9yIGlkZW50aWZpY2F0aW9uIHB1cnBvc2VzIGFuZCBkbyBub3QgaGF2ZSBtYXRoZW1hdGljYWwgc2lnbmlmaWNhbmNlLiBBbmQgc2luY2UgdGhleSBhcmUgdW5pcXVlIHVucmVwZXRpdGl2ZSBzdHJpbmdzIG9mIG51bWJlcnMsIGEgYmFycGxvdCBpcyBub3QgbmVjZXNzYXJ5IHRvIHJlcHJlc2VudCB0aGVtLiBJbiBhZGRpdGlvbiwgdGhleSB3aWxsIGJlIHJlbW92ZWQgbGF0ZXIgaW4gdGhlIGRhdGEgcmVkdWN0aW9uIHN0ZXAuIAoKTW9yZW92ZXIsIGF0dHJpYnV0ZXMgZm91bmRlZF9hdCwJY2xvc2VkX2F0LAlmaXJzdF9mdW5kaW5nX2F0LCBhbmQJbGFzdF9mdW5kaW5nX2F0IHJlcHJlc2VudCBkYXRlcyBpbiB0aGUgZm9ybWF0IG1tL3l5L2RkLiBXZSBjYW4gc2F5IHRoYXQgdGhleSBhcmUgc2VtaS11bmlxdWUgc2luY2UgdGhlIHByb2JhYmlsaXR5IG9mIHR3byByb3dzIHNoYXJpbmcgdGhlIHNhbWUgZGF0ZSBpcyBleHRyZW1lbHkgbG93IGFuZCBob2xkcyBubyBzaWduaWZpY2FuY2UuIFRoZXJlZm9yZSwgbm8gYmFycGxvdCBpcyBuZWNlc3NhcnkgdG8gcmVwcmVzZW50IHRoZW0uICAKCmBgYHtyfQpsaWJyYXJ5KHRpZHlyKQoKIyBTZWxlY3Rpbmcgc3BlY2lmaWMgY29sdW1ucyBmb3IgdmlzdWFsaXphdGlvbgpjb2x1bW5zX3RvX3Zpc3VhbGl6ZSA8LSBkYXRhc2V0WywgYygic3RhdGVfY29kZSIsICJjaXR5IiwgImNhdGVnb3J5X2NvZGUiLCAic3RhdGVfY29kZS4xIildCgojIE1lbHQgdGhlIHJlcXVpcmVkIGNvbHVtbnMgZm9yIHZpc3VhbGl6YXRpb24KbWVsdGVkX2RhdGFfYmVmb3JlIDwtIGdhdGhlcihkYXRhID0gY29sdW1uc190b192aXN1YWxpemUpCgojIFBsb3R0aW5nIGJhciBncmFwaHMgZm9yIHRoZSBzcGVjaWZpZWQgYXR0cmlidXRlcyBhbmQgZmFjZXRfd3JhcApiYXJwbG90X25vbWluYWxfYmVmb3JlIDwtIGdncGxvdChtZWx0ZWRfZGF0YV9iZWZvcmUsIGFlcyh4ID0gdmFsdWUsIGZpbGwgPSBrZXkpKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gImNvdW50IiwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtncmF5IikgKwogIGZhY2V0X3dyYXAofmtleSwgc2NhbGVzID0gImZyZWUiKSArCiAgbGFicyh0aXRsZSA9ICJOb21pbmFsIEF0dHJpYnV0ZXMgQkVGT1JFIFByZS1wcm9jZXNzaW5nIiwgeCA9ICJWYWx1ZXMiLCB5ID0gIkNvdW50IikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKIyBEaXNwbGF5aW5nIHRoZSBwbG90CnByaW50KGJhcnBsb3Rfbm9taW5hbF9iZWZvcmUpCmBgYApGcm9tIHRoZSBncmFwaHMsIHdlIGNhbiBzZWUgdGhhdCBzdGF0ZV9jb2RlIGFuZCBzdGF0ZV9jb2RlLjEgYXJlIHJlZHVuZGFudCB3aGljaCByZXF1aXJlcyB0aGUgZWxpbWluYXRpb24gb2Ygb25lIG9mIHRoZW0gbGF0ZXIgaW4gdGhlIGRhdGEgcmVkdWN0aW9uIHN0ZXAuIEluIGFkZGl0aW9uLCB3ZSBjYW4gc2VlIHdoaWNoIHN0YXRlIGNvZGUsIGNpdHksIGFuZCBjYXRlZ29yeSBjb2RlIG1vc3Qgc3RhcnR1cHMgc2hhcmVkLiAKCkF0dHJpYnV0ZXMgVW5uYW1lZDogMCwgaWQsIHppcF9jb2RlLCBVbm5hbWVkOiA2LCBhbmQgb2JqZWN0X2lkIGNhbm5vdCBiZSBwbG90dGVkIGFzIHRoZXkgYXJlIHVuaXF1ZSBhdHRyaWJ1dGVzLiBUaGV5IHBlcnRhaW4gbm8gc2lnbmlmaWNhbmNlIHRvIHRoZSBkYXRhc2V0IGFuZCB3aWxsIGJlIHJlbW92ZWQgbGF0ZXIgb24gaW4gZGF0YSByZWR1Y3Rpb24uIDxicj4gCgojIyMjIEMpIEJpbmFyeSBBdHRyaWJ1dGVzIEJhcnBsb3Q6CgpgYGB7cn0KbGlicmFyeSh0aWR5cikKCiMgTGlzdCBvZiBiaW5hcnkgYXR0cmlidXRlcwpiaW5hcnlfYXR0cmlidXRlcyA8LSBjKCJpc19DQSIsICJpc19OWSIsICJpc19NQSIsICJpc19UWCIsICJpc19vdGhlcnN0YXRlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgImlzX3NvZnR3YXJlIiwgImlzX3dlYiIsICJpc19tb2JpbGUiLCAiaXNfZW50ZXJwcmlzZSIsIAogICAgICAgICAgICAgICAgICAgICAgICJpc19hZHZlcnRpc2luZyIsICJpc19nYW1lc3ZpZGVvIiwgImlzX2Vjb21tZXJjZSIsIAogICAgICAgICAgICAgICAgICAgICAgICJpc19iaW90ZWNoIiwgImlzX2NvbnN1bHRpbmciLCAiaXNfb3RoZXJjYXRlZ29yeSIsIAogICAgICAgICAgICAgICAgICAgICAgICJoYXNfVkMiLCAiaGFzX2FuZ2VsIiwgImhhc19yb3VuZEEiLCAiaGFzX3JvdW5kQiIsIAogICAgICAgICAgICAgICAgICAgICAgICJoYXNfcm91bmRDIiwgImhhc19yb3VuZEQiLCAibGFiZWxzIiwgImlzX3RvcDUwMCIsICJzdGF0dXMiKQoKIyBNZWx0IHRoZSBkYXRhc2V0cyBmb3IgZWFzaWVyIHBsb3R0aW5nCm1lbHRlZF9kYXRhIDwtIGdhdGhlcihkYXRhc2V0LCBrZXkgPSAidmFyaWFibGUiLCB2YWx1ZSA9ICJ2YWx1ZSIsIGFsbF9vZihiaW5hcnlfYXR0cmlidXRlcykpCgojIENyZWF0ZSBiYXIgcGxvdHMgd2l0aCBmYWNldCB3cmFwCmJhcnBsb3RfYmluYXJ5X2JlZm9yZSA8LSBnZ3Bsb3QobWVsdGVkX2RhdGEsIGFlcyh4ID0gdmFsdWUsIGZpbGwgPSB2YXJpYWJsZSkpICsKICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAiY291bnQiLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya2dyYXkiKSArCiAgZmFjZXRfd3JhcCh+dmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIikgKwogIGxhYnModGl0bGUgPSAiQmFyIFBsb3RzIGZvciBCaW5hcnkgQXR0cmlidXRlcyBCRUZPUkUgUHJlLXByb2Nlc3NpbmciLCB4ID0gIlZhbHVlIiwgeSA9ICJDb3VudCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChiYXJwbG90X2JpbmFyeV9iZWZvcmUpCmBgYApUaGUgYmFycGxvdHMgcHJvdmlkZSBpbnNpZ2h0cyBpbnRvIHRoZSBzdGFydHVwIGxhbmRzY2FwZSBmcm9tIDE5ODQgdG8gMjAxMCwgcmV2ZWFsaW5nIHRyZW5kcyBzdWNoIGFzIHRoZSBzdGF0ZXMgd2l0aCB0aGUgaGlnaGVzdCBzdGFydHVwIGFjdGl2aXR5LCB0aGUgbW9zdCBwcmV2YWxlbnQgY2F0ZWdvcnkgdHlwZXMsIHByZWZlcnJlZCBpbnZlc3RtZW50IHJvdW5kcywgdGhlIGxpa2VsaWhvb2Qgb2YgYSBzdGFydHVwIGJlY29taW5nIGEgdG9wIDUwMCBjb21wYW55LCBhbmQgdGhlaXIgb3ZlcmFsbCBzdGF0dXMuPGJyPjxicj48YnI+CgoKIyMgNC1EYXRhIFByZS1wcm9jZXNzaW5nIAoKRGF0YSBwcmUtcHJvY2VzaW5nIGlzIGVzc2VudGlhbCBpbiBwcm9kdWNpbmcgYWNjdXJhdGUgcmVzdWx0cy4gQmVjYXVzZSBjb3JyZWN0IHByZS1wcm9jZXNzaW5nIGxlYWRzIHRvIGNvcnJlY3QgcmVzdWx0cywgYW5kIHZpY2UgdmVyc2EuIFdlIHdpbGwgcHJlcGFyZSB0aGUgc3RhcnR1cCBkYXRhIGZvciBkYXRhIG1pbmluZyBieSBmb2xsb3dpbmcgdGhlIGRhdGEgcHJlLXByb2Nlc3Npbmcgc3RlcHM6IAoxLURhdGEgQ2xlYW5pbmcgCjItRGF0YSBJbnRlZ3JhdGlvbgozLURhdGEgUmVkdWN0aW9uCjQtRGF0YSBUcmFuc2Zvcm1hdGlvbgo8YnI+PGJyPgoKIyMgNC4xLURhdGEgQ2xlYW5pbmcgCgpJbiBkYXRhIGNsZWFuaW5nIHdlIGFyZSBlbnN1cmluZyBhY2N1cmFjeSBhbmQgcmVsaWFiaWxpdHkgYnkgaWRlbnRpZnlpbmcgcmVwbGFjaW5nIG5lZ2F0aXZlIHZhbHVlcyBhbmQgcmVtb3Zpbmcgb3V0bGllcnMgZnJvbSB0aGUgZGF0YXNldC4gPGJyPgoKIyMjIEEpIE5lZ2F0aXZlIFZhbHVlcwoKTmVnYXRpdmUgdmFsdWVzIGluIGF0dHJpYnV0ZXMgcmVsYXRlZCB0byBhZ2UgYW5kIGxvbmdpdHVkZSBkbyBub3QgbWFrZSBzZW5zZS4gU28sIHdlIGFyZSBnb2luZyB0byByZW1vdmUgdGhlIG5lZ2F0aXZlIHNpZ24gZnJvbSB0aGUgdmFsdWVzLiAKCiMjIyMjIEJlZm9yZSByZW1vdmluZyBuZWdhdGl2ZSBzaWduIG9mIGF0dHJpYnV0ZXMgYWdlX2ZpcnN0X2Z1bmRpbmdfeWVhciwgYWdlX2xhc3RfZnVuZGluZ195ZWFyLCBhZ2VfZmlyc3RfbWlsZXN0b25lX3llYXIsIGFnZV9sYXN0X21pbGVzdG9uZV95ZWFyLCBhbmQgbG9uZ2l0dWRlOgpgYGB7cn0Kcm93XzIzNSA8LSBkYXRhc2V0WzIzNSwgYygibG9uZ2l0dWRlIiwgImFnZV9maXJzdF9mdW5kaW5nX3llYXIiLCAiYWdlX2xhc3RfZnVuZGluZ195ZWFyIiwgImFnZV9maXJzdF9taWxlc3RvbmVfeWVhciIsICJhZ2VfbGFzdF9taWxlc3RvbmVfeWVhciIpXQoKIyBEaXNwbGF5IHRoZSByZXN1bHQKcHJpbnQocm93XzIzNSkKYGBgCkFzIGRlcGljdGVkLCBhdHRyaWJ1dGVzIGFib3ZlIGNvbnRhaW4gbmVnYXRpdmUgdmFsdWVzLiAKCiMjIyMjIFJlbW92aW5nIG5lZ2F0aXZlIHZhbHVlczoKYGBge3J9CiMgUmVtb3ZlIHRoZSBuZWdhdGl2ZSBzaWduIGZyb20gYWdlX2ZpcnN0X2Z1bmRpbmdfeWVhcgpkYXRhc2V0JGFnZV9maXJzdF9mdW5kaW5nX3llYXIgPC0gYWJzKGRhdGFzZXQkYWdlX2ZpcnN0X2Z1bmRpbmdfeWVhcikKCiMgUmVtb3ZlIHRoZSBuZWdhdGl2ZSBzaWduIGZyb20gYWdlX2xhc3RfZnVuZGluZ195ZWFyCmRhdGFzZXQkYWdlX2xhc3RfZnVuZGluZ195ZWFyIDwtIGFicyhkYXRhc2V0JGFnZV9sYXN0X2Z1bmRpbmdfeWVhcikKCiMgUmVtb3ZlIHRoZSBuZWdhdGl2ZSBzaWduIGZyb20gYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyCmRhdGFzZXQkYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyIDwtIGFicyhkYXRhc2V0JGFnZV9maXJzdF9taWxlc3RvbmVfeWVhcikKCiMgUmVtb3ZlIHRoZSBuZWdhdGl2ZSBzaWduIGZyb20gYWdlX2xhc3RfbWlsZXN0b25lX3llYXIKZGF0YXNldCRhZ2VfbGFzdF9taWxlc3RvbmVfeWVhciA8LSBhYnMoZGF0YXNldCRhZ2VfbGFzdF9taWxlc3RvbmVfeWVhcikKCiMgUmVtb3ZlIHRoZSBuZWdhdGl2ZSBzaWduIGZyb20gbG9uZ2l0dWRlCmRhdGFzZXQkbG9uZ2l0dWRlIDwtIGFicyhkYXRhc2V0JGxvbmdpdHVkZSkKYGBgClRoaXMgY29kZSBjaHVuayByZW1vdmVzIHRoZSBuZWdhdGl2ZSBzaWduIGZyb20gdGhlIGF0dHJpYnV0ZXMuIAoKIyMjIyMgQWZ0ZXIgcmVtb3ZpbmcgbmVnYXRpdmUgc2lnbiBvZiBhdHRyaWJ1dGVzIGFnZV9maXJzdF9mdW5kaW5nX3llYXIsIGFnZV9sYXN0X2Z1bmRpbmdfeWVhciwgYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyLCBhZ2VfbGFzdF9taWxlc3RvbmVfeWVhciwgYW5kIGxvbmdpdHVkZToKYGBge3J9CnJvd18yMzUgPC0gZGF0YXNldFsyMzUsIGMoImxvbmdpdHVkZSIsICJhZ2VfZmlyc3RfZnVuZGluZ195ZWFyIiwgImFnZV9sYXN0X2Z1bmRpbmdfeWVhciIsICJhZ2VfZmlyc3RfbWlsZXN0b25lX3llYXIiLCAiYWdlX2xhc3RfbWlsZXN0b25lX3llYXIiKV0KCiMgRGlzcGxheSB0aGUgcmVzdWx0CnByaW50KHJvd18yMzUpCmBgYApBcyBzaG93biwgbm9uZSBvZiB0aGUgYXR0cmlidXRlcyBhYm92ZSBoYXZlIGEgbmVnYXRpdmUgc2lnbiBhcyB0aGV5IGhhdmUgYWxsIGJlY29tZSBwb3NpdGl2ZSBudW1iZXJzLiA8YnI+CgoKIyMjIEIpIE91dGxpZXJzIAoKV2Ugd2lsbCBpZGVudGlmeSBhbmQgZWxpbWluYXRlIG91dGxpZXJzIHByZXNlbnQgaW4gdGhlIG51bWVyaWNhbCBhdHRyaWJ1dGVzIG9mIHRoZSBkYXRhc2V0LiBPdXRsaWVycywgb3IgZGF0YSBwb2ludHMgdGhhdCBzaWduaWZpY2FudGx5IGRldmlhdGUgZnJvbSB0aGUgbWFqb3JpdHksIGNhbiBza2V3IHN0YXRpc3RpY2FsIGFuYWx5c2VzIGFuZCBhZmZlY3QgdGhlIGFjY3VyYWN5IG9mIG1vZGVscy4gQnkgZGV0ZWN0aW5nIGFuZCByZW1vdmluZyB0aGVzZSBvdXRsaWVycywgd2UgYWltIHRvIGVuc3VyZSBhIG1vcmUgcm9idXN0IGFuZCByZXByZXNlbnRhdGl2ZSBkYXRhc2V0IGZvciBzdWJzZXF1ZW50IGFuYWx5c2VzLgoKQmluYXJ5IGFuZCBub21pbmFsIGF0dHJpYnV0ZXMsIGFyZSBkaXNjcmV0ZSBhbmQgY2F0ZWdvcmljYWwuIFRoZXkgZG9uJ3QgZXhoaWJpdCBvdXRsaWVycyBhcyBudW1lcmljYWwgdmFsdWVzIGRvLiBPdXRsaWVycyBhcmUgc3BlY2lmaWMgdG8gbnVtZXJpY2FsIGRhdGEgd2hlcmUgdmFsdWVzIHNpZ25pZmljYW50bHkgZGV2aWF0ZSBmcm9tIHRoZSByZXN0IG9mIHRoZSBkYXRhc2V0LiBJbiB0aGUgY2FzZSBvZiBiaW5hcnkgYXR0cmlidXRlcywgdGhlc2UgcmVwcmVzZW50IHR3byBjYXRlZ29yaWVzICgwIG9yIDEpLCBzbyB0aGVyZSBhcmVuJ3Qgb3V0bGllcnMgYXMgdGhlcmUncyBubyBudW1lcmljYWwgcmFuZ2Ugb3Igc2VxdWVuY2UgdG8gbWVhc3VyZSBleHRyZW1lcy4gRm9yIG5vbWluYWwgYXR0cmlidXRlcywgd2hpY2ggcmVwcmVzZW50IGNhdGVnb3JpZXMgd2l0aG91dCBpbmhlcmVudCBvcmRlciAobGlrZSBuYW1lcykgb3V0bGllcnMgZG9uJ3QgZXhpc3QgaW4gdGhlIHNhbWUgc2Vuc2UgYXMgdGhleSBkbyBpbiBudW1lcmljYWwgZGF0YS4gT3V0bGllcnMgaW4gbnVtZXJpY2FsIGRhdGEgbWlnaHQgc3VnZ2VzdCBlcnJvcnMsIGFub21hbGllcywgb3IgZXh0cmVtZXMgaW4gdGhlIGRhdGEuIAoKIyMjIyMgTnVtZXJpY2FsIEF0dHJpYnV0ZSBCb3hwbG90cyBCZWZvcmUgUmVtb3ZpbmcgT3V0bGllcnM6CgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKIyBTZWxlY3QgbnVtZXJpY2FsIGF0dHJpYnV0ZXMgZm9yIGJveHBsb3RzCm51bWVyaWNhbF9hdHRyaWJ1dGVzIDwtIGMoImxhdGl0dWRlIiwgImxvbmdpdHVkZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhZ2VfZmlyc3RfZnVuZGluZ195ZWFyIiwgImFnZV9sYXN0X2Z1bmRpbmdfeWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhZ2VfZmlyc3RfbWlsZXN0b25lX3llYXIiLCAiYWdlX2xhc3RfbWlsZXN0b25lX3llYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicmVsYXRpb25zaGlwcyIsICJmdW5kaW5nX3JvdW5kcyIsICJmdW5kaW5nX3RvdGFsX3VzZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJtaWxlc3RvbmVzIiwgImF2Z19wYXJ0aWNpcGFudHMiKQoKIyBNZWx0IHRoZSBkYXRhc2V0IGZvciBlYXNpZXIgcGxvdHRpbmcKbWVsdGVkX2RhdGEgPC0gcmVzaGFwZTI6Om1lbHQoZGF0YXNldFssIG51bWVyaWNhbF9hdHRyaWJ1dGVzXSkKCiMgQ3JlYXRlIGJveHBsb3RzIHdpdGggZmFjZXQgd3JhcApib3hwbG90X3Bsb3QgPC0gZ2dwbG90KG1lbHRlZF9kYXRhLCBhZXMoeCA9IHZhcmlhYmxlLCB5ID0gdmFsdWUpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiZGFya2dyYXkiLCBjb2xvciA9ICJibGFjayIpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArCiAgbGFicyh0aXRsZSA9ICJCb3hwbG90cyBmb3IgTnVtZXJpY2FsIEF0dHJpYnV0ZXMgQmVmb3JlIFJlbW92aW5nIE91dGxpZXJzIiwKICAgICAgIHggPSAiQXR0cmlidXRlIiwKICAgICAgIHkgPSAiVmFsdWUiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgojIERpc3BsYXkgdGhlIHBsb3QKcHJpbnQoYm94cGxvdF9wbG90KQoKYGBgClRoZSBib3ggcmVwcmVzZW50cyB0aGUgaW50ZXJxdWFydGlsZSByYW5nZSAoSVFSKSwgd2hpY2ggaXMgdGhlIHJhbmdlIGJldHdlZW4gdGhlIGZpcnN0IHF1YXJ0aWxlIChRMSkgYW5kIHRoZSB0aGlyZCBxdWFydGlsZSAoUTMpLiBUaGUgbGVuZ3RoIG9mIHRoZSBib3ggaW5kaWNhdGVzIHRoZSBzcHJlYWQgb2YgdGhlIG1pZGRsZSA1MCUgb2YgdGhlIGRhdGEuIFRoZSB3aGlza2VycyBleHRlbmQgZnJvbSB0aGUgYm94IHRvIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHZhbHVlcyB3aXRoaW4gYSBjZXJ0YWluIHJhbmdlLiBCeSBkZWZhdWx0LCB0aGlzIHJhbmdlIGlzIDEuNSB0aW1lcyB0aGUgSVFSLiBQb2ludHMgYmV5b25kIHRoZSB3aGlza2VycyBhcmUgb3V0bGllcnMuIEFzIGRlbW9uc3RyYXRlZCBhYm92ZSwgZXZlcnkgYm94cGxvdCBjb250YWlucyBvdXRsaWVycyB0aGF0IGZhbGwgYmV5b25kIHRoZSB3aGlza2VycyByZXByZXNlbnRlZCBieSBpbmRpdmlkdWFsIHBvaW50cy4gCgojIyMjIyBPdmVydmlldyBvZiBvdXRsaWVyczogCgpgYGB7cn0KIyBBdHRyaWJ1dGVzIHRvIGNoZWNrIGZvciBvdXRsaWVycwphdHRyaWJ1dGVzX3RvX2NoZWNrIDwtIGMoImxhdGl0dWRlIiwgImxvbmdpdHVkZSIsICJhZ2VfZmlyc3RfZnVuZGluZ195ZWFyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAiYWdlX2xhc3RfZnVuZGluZ195ZWFyIiwgImFnZV9maXJzdF9taWxlc3RvbmVfeWVhciIsIAogICAgICAgICAgICAgICAgICAgICAgICAgImFnZV9sYXN0X21pbGVzdG9uZV95ZWFyIiwgInJlbGF0aW9uc2hpcHMiLCAiZnVuZGluZ19yb3VuZHMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICJmdW5kaW5nX3RvdGFsX3VzZCIsICJtaWxlc3RvbmVzIiwgImF2Z19wYXJ0aWNpcGFudHMiKQoKIyBDYWxjdWxhdGUgdGhlIG51bWJlciBvZiBvdXRsaWVycyBmb3IgZWFjaCBhdHRyaWJ1dGUKb3V0bGllcl9jb3VudHMgPC0gc2FwcGx5KGF0dHJpYnV0ZXNfdG9fY2hlY2ssIGZ1bmN0aW9uKGF0dHIpIHsKICBRMSA8LSBxdWFudGlsZShkYXRhc2V0W1thdHRyXV0sIDAuMjUsIG5hLnJtID0gVFJVRSkKICBRMyA8LSBxdWFudGlsZShkYXRhc2V0W1thdHRyXV0sIDAuNzUsIG5hLnJtID0gVFJVRSkKICBJUVIgPC0gUTMgLSBRMQogIGxvd2VyX2JvdW5kIDwtIFExIC0gMS41ICogSVFSCiAgdXBwZXJfYm91bmQgPC0gUTMgKyAxLjUgKiBJUVIKICBzdW0oZGF0YXNldFtbYXR0cl1dIDwgbG93ZXJfYm91bmQgfCBkYXRhc2V0W1thdHRyXV0gPiB1cHBlcl9ib3VuZCwgbmEucm0gPSBUUlVFKQp9KQoKIyBDcmVhdGUgYSB0YWJsZSB3aXRoIGF0dHJpYnV0ZSBuYW1lcyBhbmQgdGhlaXIgcmVzcGVjdGl2ZSBvdXRsaWVyIGNvdW50cwpvdXRsaWVyX3RhYmxlIDwtIGRhdGEuZnJhbWUoQXR0cmlidXRlID0gYXR0cmlidXRlc190b19jaGVjaywgT3V0bGllcl9Db3VudCA9IG91dGxpZXJfY291bnRzKQpwcmludChvdXRsaWVyX3RhYmxlKQpgYGAKVGhlIHRhYmxlIGFib3ZlIHJlcHJlc2VudHMgdGhlIG51bWJlciBvZiBvdXRsaWVycyBmb3IgZWFjaCBhdHRyaWJ1dGUuIAoKCiMjIyMjIFJlbW92aW5nIE91dGxpZXJzOiAKCmBgYHtyfQojIyBhZ2VfZmlyc3RfZnVuZGluZ195ZWFyCgojIENhbGN1bGF0ZSB0aGUgcXVhcnRpbGVzClExIDwtIHF1YW50aWxlKGRhdGFzZXQkYWdlX2ZpcnN0X2Z1bmRpbmdfeWVhciwgMC4yNSkKUTMgPC0gcXVhbnRpbGUoZGF0YXNldCRhZ2VfZmlyc3RfZnVuZGluZ195ZWFyLCAwLjc1KQoKIyBDYWxjdWxhdGUgdGhlIElRUgpJUVIgPC0gUTMgLSBRMQoKIyBEZWZpbmUgdGhlIGxvd2VyIGFuZCB1cHBlciBib3VuZHMgZm9yIG91dGxpZXJzCmxvd2VyX2JvdW5kIDwtIFExIC0gMS41ICogSVFSCnVwcGVyX2JvdW5kIDwtIFEzICsgMS41ICogSVFSCgojIFJlbW92ZSBvdXRsaWVycyBmcm9tIHRoZSBkYXRhc2V0IHVzaW5nIGRwbHlyCmxpYnJhcnkoZHBseXIpCgpkYXRhc2V0X2NsZWFuMSA8LSBkYXRhc2V0ICU+JQogIGZpbHRlcihhZ2VfZmlyc3RfZnVuZGluZ195ZWFyID49IGxvd2VyX2JvdW5kLCBhZ2VfZmlyc3RfZnVuZGluZ195ZWFyIDw9IHVwcGVyX2JvdW5kKQoKIyBQcmludCB0aGUgZGltZW5zaW9ucyBvZiB0aGUgY2xlYW5lZCBkYXRhc2V0CmNhdCgiZnVuZGluZ190b3RhbF91c2QiLCJcbiIpCmNhdCgiT3JpZ2luYWwgZGF0YXNldCBkaW1lbnNpb25zOiAiLCBkaW0oZGF0YXNldCksICJcbiIpCmNhdCgiQ2xlYW5lZCBkYXRhc2V0IGRpbWVuc2lvbnM6ICIsIGRpbShkYXRhc2V0X2NsZWFuMSksICJcbiIsIlxuIikKZGF0YXNldCA8LSBkYXRhc2V0X2NsZWFuMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIGFnZV9sYXN0X2Z1bmRpbmdfeWVhcgoKUTEgPC0gcXVhbnRpbGUoZGF0YXNldCRhZ2VfbGFzdF9mdW5kaW5nX3llYXIsIDAuMjUpClEzIDwtIHF1YW50aWxlKGRhdGFzZXQkYWdlX2xhc3RfZnVuZGluZ195ZWFyLCAwLjc1KQpJUVIgPC0gUTMgLSBRMQpsb3dlcl9ib3VuZCA8LSBRMSAtIDEuNSAqIElRUgp1cHBlcl9ib3VuZCA8LSBRMyArIDEuNSAqIElRUgpsaWJyYXJ5KGRwbHlyKQpkYXRhc2V0X2NsZWFuMiA8LSBkYXRhc2V0ICU+JQogIGZpbHRlcihhZ2VfbGFzdF9mdW5kaW5nX3llYXIgPj0gbG93ZXJfYm91bmQsIGFnZV9sYXN0X2Z1bmRpbmdfeWVhciA8PSB1cHBlcl9ib3VuZCkKCmNhdCgiYWdlX2xhc3RfZnVuZGluZ195ZWFyIiwiXG4iKQpjYXQoIk9yaWdpbmFsIGRhdGFzZXQgZGltZW5zaW9uczogIiwgZGltKGRhdGFzZXQpLCAiXG4iKQpjYXQoIkNsZWFuZWQgZGF0YXNldCBkaW1lbnNpb25zOiAiLCBkaW0oZGF0YXNldF9jbGVhbjIpLCAiXG4iLCJcbiIpCmRhdGFzZXQgPC0gZGF0YXNldF9jbGVhbjIKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyBhZ2VfZmlyc3RfbWlsZXN0b25lX3llYXIKClExIDwtIHF1YW50aWxlKGRhdGFzZXQkYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyLCAwLjI1KQpRMyA8LSBxdWFudGlsZShkYXRhc2V0JGFnZV9maXJzdF9taWxlc3RvbmVfeWVhciwgMC43NSkKSVFSIDwtIFEzIC0gUTEKbG93ZXJfYm91bmQgPC0gUTEgLSAxLjUgKiBJUVIKdXBwZXJfYm91bmQgPC0gUTMgKyAxLjUgKiBJUVIKbGlicmFyeShkcGx5cikKZGF0YXNldF9jbGVhbjMgPC0gZGF0YXNldCAlPiUKICBmaWx0ZXIoYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyID49IGxvd2VyX2JvdW5kLCBhZ2VfZmlyc3RfbWlsZXN0b25lX3llYXIgPD0gdXBwZXJfYm91bmQpCgpjYXQoImFnZV9maXJzdF9taWxlc3RvbmVfeWVhciIsIlxuIikKY2F0KCJPcmlnaW5hbCBkYXRhc2V0IGRpbWVuc2lvbnM6ICIsIGRpbShkYXRhc2V0KSwgIlxuIikKY2F0KCJDbGVhbmVkIGRhdGFzZXQgZGltZW5zaW9uczogIiwgZGltKGRhdGFzZXRfY2xlYW4zKSwgIlxuIiwiXG4iKQpkYXRhc2V0IDwtIGRhdGFzZXRfY2xlYW4zCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMgYWdlX2xhc3RfbWlsZXN0b25lX3llYXIKClExIDwtIHF1YW50aWxlKGRhdGFzZXQkYWdlX2xhc3RfbWlsZXN0b25lX3llYXIsIDAuMjUpClEzIDwtIHF1YW50aWxlKGRhdGFzZXQkYWdlX2xhc3RfbWlsZXN0b25lX3llYXIsIDAuNzUpCklRUiA8LSBRMyAtIFExCmxvd2VyX2JvdW5kIDwtIFExIC0gMS41ICogSVFSCnVwcGVyX2JvdW5kIDwtIFEzICsgMS41ICogSVFSCmxpYnJhcnkoZHBseXIpCmRhdGFzZXRfY2xlYW40IDwtIGRhdGFzZXQgJT4lCiAgZmlsdGVyKGFnZV9sYXN0X21pbGVzdG9uZV95ZWFyID49IGxvd2VyX2JvdW5kLCBhZ2VfbGFzdF9taWxlc3RvbmVfeWVhciA8PSB1cHBlcl9ib3VuZCkKCmNhdCgiYWdlX2xhc3RfbWlsZXN0b25lX3llYXIiLCJcbiIpCmNhdCgiT3JpZ2luYWwgZGF0YXNldCBkaW1lbnNpb25zOiAiLCBkaW0oZGF0YXNldCksICJcbiIpCmNhdCgiQ2xlYW5lZCBkYXRhc2V0IGRpbWVuc2lvbnM6ICIsIGRpbShkYXRhc2V0X2NsZWFuNCksICJcbiIsIlxuIikKZGF0YXNldCA8LSBkYXRhc2V0X2NsZWFuNAoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIHJlbGF0aW9uc2hpcHMKClExIDwtIHF1YW50aWxlKGRhdGFzZXQkcmVsYXRpb25zaGlwcywgMC4yNSkKUTMgPC0gcXVhbnRpbGUoZGF0YXNldCRyZWxhdGlvbnNoaXBzLCAwLjc1KQpJUVIgPC0gUTMgLSBRMQpsb3dlcl9ib3VuZCA8LSBRMSAtIDEuNSAqIElRUgp1cHBlcl9ib3VuZCA8LSBRMyArIDEuNSAqIElRUgpsaWJyYXJ5KGRwbHlyKQpkYXRhc2V0X2NsZWFuNSA8LSBkYXRhc2V0ICU+JQogIGZpbHRlcihyZWxhdGlvbnNoaXBzID49IGxvd2VyX2JvdW5kLCByZWxhdGlvbnNoaXBzIDw9IHVwcGVyX2JvdW5kKQoKY2F0KCJyZWxhdGlvbnNoaXBzIiwiXG4iKQpjYXQoIk9yaWdpbmFsIGRhdGFzZXQgZGltZW5zaW9uczogIiwgZGltKGRhdGFzZXQpLCAiXG4iKQpjYXQoIkNsZWFuZWQgZGF0YXNldCBkaW1lbnNpb25zOiAiLCBkaW0oZGF0YXNldF9jbGVhbjUpLCAiXG4iLCJcbiIpCmRhdGFzZXQgPC0gZGF0YXNldF9jbGVhbjUKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyBmdW5kaW5nX3JvdW5kcwoKUTEgPC0gcXVhbnRpbGUoZGF0YXNldCRmdW5kaW5nX3JvdW5kcywgMC4yNSkKUTMgPC0gcXVhbnRpbGUoZGF0YXNldCRmdW5kaW5nX3JvdW5kcywgMC43NSkKSVFSIDwtIFEzIC0gUTEKbG93ZXJfYm91bmQgPC0gUTEgLSAxLjUgKiBJUVIKdXBwZXJfYm91bmQgPC0gUTMgKyAxLjUgKiBJUVIKbGlicmFyeShkcGx5cikKZGF0YXNldF9jbGVhbjYgPC0gZGF0YXNldCAlPiUKICBmaWx0ZXIoZnVuZGluZ19yb3VuZHMgPj0gbG93ZXJfYm91bmQsIGZ1bmRpbmdfcm91bmRzIDw9IHVwcGVyX2JvdW5kKQoKY2F0KCJmdW5kaW5nX3JvdW5kcyIsIlxuIikKY2F0KCJPcmlnaW5hbCBkYXRhc2V0IGRpbWVuc2lvbnM6ICIsIGRpbShkYXRhc2V0KSwgIlxuIikKY2F0KCJDbGVhbmVkIGRhdGFzZXQgZGltZW5zaW9uczogIiwgZGltKGRhdGFzZXRfY2xlYW42KSwgIlxuIiwiXG4iKQpkYXRhc2V0IDwtIGRhdGFzZXRfY2xlYW42CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMgZnVuZGluZ190b3RhbF91c2QKClExIDwtIHF1YW50aWxlKGRhdGFzZXQkZnVuZGluZ190b3RhbF91c2QsIDAuMjUpClEzIDwtIHF1YW50aWxlKGRhdGFzZXQkZnVuZGluZ190b3RhbF91c2QsIDAuNzUpCklRUiA8LSBRMyAtIFExCmxvd2VyX2JvdW5kIDwtIFExIC0gMS41ICogSVFSCnVwcGVyX2JvdW5kIDwtIFEzICsgMS41ICogSVFSCmxpYnJhcnkoZHBseXIpCmRhdGFzZXRfY2xlYW43IDwtIGRhdGFzZXQgJT4lCiAgZmlsdGVyKGZ1bmRpbmdfdG90YWxfdXNkID49IGxvd2VyX2JvdW5kLCBmdW5kaW5nX3RvdGFsX3VzZCA8PSB1cHBlcl9ib3VuZCkKCmNhdCgiZnVuZGluZ190b3RhbF91c2QiLCJcbiIpCmNhdCgiT3JpZ2luYWwgZGF0YXNldCBkaW1lbnNpb25zOiAiLCBkaW0oZGF0YXNldCksICJcbiIpCmNhdCgiQ2xlYW5lZCBkYXRhc2V0IGRpbWVuc2lvbnM6ICIsIGRpbShkYXRhc2V0X2NsZWFuNyksICJcbiIsIlxuIikKZGF0YXNldCA8LSBkYXRhc2V0X2NsZWFuNwoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIG1pbGVzdG9uZXMKClExIDwtIHF1YW50aWxlKGRhdGFzZXQkbWlsZXN0b25lcywgMC4yNSkKUTMgPC0gcXVhbnRpbGUoZGF0YXNldCRtaWxlc3RvbmVzLCAwLjc1KQpJUVIgPC0gUTMgLSBRMQpsb3dlcl9ib3VuZCA8LSBRMSAtIDEuNSAqIElRUgp1cHBlcl9ib3VuZCA8LSBRMyArIDEuNSAqIElRUgpsaWJyYXJ5KGRwbHlyKQpkYXRhc2V0X2NsZWFuOCA8LSBkYXRhc2V0ICU+JQogIGZpbHRlcihtaWxlc3RvbmVzID49IGxvd2VyX2JvdW5kLCBtaWxlc3RvbmVzIDw9IHVwcGVyX2JvdW5kKQoKY2F0KCJtaWxlc3RvbmVzIiwiXG4iKQpjYXQoIk9yaWdpbmFsIGRhdGFzZXQgZGltZW5zaW9uczogIiwgZGltKGRhdGFzZXQpLCAiXG4iKQpjYXQoIkNsZWFuZWQgZGF0YXNldCBkaW1lbnNpb25zOiAiLCBkaW0oZGF0YXNldF9jbGVhbjgpLCAiXG4iLCJcbiIpCgpkYXRhc2V0IDwtIGRhdGFzZXRfY2xlYW44CmBgYApUaGlzIGNvZGUgY2h1bmsgcmVtb3ZlcyBvdXRsaWVyIG51bWVyaWNhbCB2YWx1ZXMgZnJvbSBhIGRhdGFzZXQsIGtlZXBpbmcgb25seSBkYXRhIHBvaW50cyB3aXRoaW4gdGhlIG5vcm1hbCByYW5nZSBpbiB0aGUgZGF0YXNldC4KCmBgYHtyfQpucm93KGRhdGFzZXQpCm5jb2woZGF0YXNldCkKYGBgCkFmdGVyIHJlbW92aW5nIG91dGxpZXJzLCB0aGVyZSBhcmUgNzI0IHJvd3MgYW5kIDQ5IGNvbHVtbnMgcmVtYWluaW5nIGluIHRoZSBkYXRhc2V0LiAKCiMjIyMjIE51bWVyaWNhbCBBdHRyaWJ1dGUgQm94cGxvdHMgQWZ0ZXIgUmVtb3ZpbmcgT3V0bGllcnM6CgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKIyBTZWxlY3QgbnVtZXJpY2FsIGF0dHJpYnV0ZXMgZm9yIGJveHBsb3RzCm51bWVyaWNhbF9hdHRyaWJ1dGVzIDwtIGMoImxhdGl0dWRlIiwgImxvbmdpdHVkZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhZ2VfZmlyc3RfZnVuZGluZ195ZWFyIiwgImFnZV9sYXN0X2Z1bmRpbmdfeWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhZ2VfZmlyc3RfbWlsZXN0b25lX3llYXIiLCAiYWdlX2xhc3RfbWlsZXN0b25lX3llYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicmVsYXRpb25zaGlwcyIsICJmdW5kaW5nX3JvdW5kcyIsICJmdW5kaW5nX3RvdGFsX3VzZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJtaWxlc3RvbmVzIiwgImF2Z19wYXJ0aWNpcGFudHMiKQoKIyBNZWx0IHRoZSBkYXRhc2V0IGZvciBlYXNpZXIgcGxvdHRpbmcKbWVsdGVkX2RhdGEgPC0gcmVzaGFwZTI6Om1lbHQoZGF0YXNldFssIG51bWVyaWNhbF9hdHRyaWJ1dGVzXSkKCiMgQ3JlYXRlIGJveHBsb3RzIHdpdGggZmFjZXQgd3JhcApib3hwbG90X3Bsb3QgPC0gZ2dwbG90KG1lbHRlZF9kYXRhLCBhZXMoeCA9IHZhcmlhYmxlLCB5ID0gdmFsdWUpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiZGFya2dyYXkiLCBjb2xvciA9ICJibGFjayIpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArCiAgbGFicyh0aXRsZSA9ICJCb3hwbG90cyBmb3IgTnVtZXJpY2FsIEF0dHJpYnV0ZXMgQWZ0ZXIgUmVtb3ZpbmcgT3V0bGllcnMiLAogICAgICAgeCA9ICJBdHRyaWJ1dGUiLAogICAgICAgeSA9ICJWYWx1ZSIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChib3hwbG90X3Bsb3QpCgpgYGAKQnkgcmVtb3ZpbmcgdGhlIGV4dHJlbWUgdmFsdWVzLCB0aGUgbmV3IHJhbmdlIGVuY29tcGFzc2VzIGEgbGFyZ2VyIHNwcmVhZCBvZiB0aGUgZGF0YSBjb21wYXJlZCB0byB0aGUgb3JpZ2luYWwgcmFuZ2Ugd2l0aCBvdXRsaWVycy4gVGhlIGRpc3RyaWJ1dGlvbiBhcHBlYXJzIG1vcmUgY29tcGFjdCBhbmQgbGVzcyBza2V3ZWQuIEhvd2V2ZXIsIHRoaXMgY2hhbmdlIG1pZ2h0IG5vdCBhbHdheXMgbGVhZCB0byBhbiBpbmNyZWFzZSBpbiB0aGUgYWN0dWFsIGRhdGEgdmFsdWVzIG9yIHRoZSB0b3RhbCByYW5nZSBidXQgcmF0aGVyIGEgY2hhbmdlIGluIHRoZSBzY2FsZSBvZiB0aGUgdmlzdWFsaXphdGlvbiBkdWUgdG8gdGhlIHJlbW92YWwgb2YgdGhlIGV4dHJlbWUgdmFsdWVzLiA8YnI+PGJyPgoKCgojIyA0LjItRGF0YSBJbnRlZ3JhdGlvbgoKSW4gZGF0YSBpbnRlZ3JhdGlvbiB3ZSBhcmUgY3JlYXRpbmcgYSB1bmlmaWVkIHZpZXcgYnkgY29tYmluaW5nIGluZm9ybWF0aW9uIGZyb20gZGl2ZXJzZSBzb3VyY2VzLiAKU2luY2UgYWxsIG9mIG91ciBkYXRhIGlzIGNvbnRhaW5lZCBpbiBvbmUgZGF0YXNldCBhbmQgd2UgYXJlIG5vdCBhZGRpbmcgdHdvIG9yIG1vcmUgY29sdW1ucyB0b2dldGhlciwgdGhlcmUgaXMgbm8gbmVlZCBmb3IgdGhlIGRhdGEgaW50ZWdyYXRpb24gc3RlcC4gPGJyPjxicj4KCgoKIyMgNC4zLURhdGEgUmVkdWN0aW9uCgpJbiB0aGUgZGF0YSByZWR1Y3Rpb24gc3RlcCwgd2UgYXJlIGVuaGFuY2luZyBlZmZpY2llbmN5IGFuZCBtb2RlbCBwZXJmb3JtYW5jZSBieSBtaW5pbWl6aW5nIGRhdGEgc2l6ZSwgZm9jdXNpbmcgb24gcmVsZXZhbnQgZmVhdHVyZXMsIGFuZCBtaXRpZ2F0aW5nIHRoZSByaXNrIG9mIG92ZXJmaXR0aW5nLiBXZSB3aWxsIGNvbmR1Y3QgdGhlIGNoaS1zcXVhcmVkIHRlc3QsIGNoZWNrIGZvciBkdXBsaWNhdGlvbiwgYW5kIHJlbW92ZSByZWR1bmRhbmN5LiA8YnI+CgojIyMgQSkgRWxpbWluYXRpb24gb2YgRHVwbGljYXRlcwoKIyMjIyBEdXBsaWNhdGUgY29sdW1ucyAKCmBgYHtyfQpuYW1lcyhkYXRhc2V0KQpgYGAKVGhlcmUgYXJlIG5vIGR1cGxpY2F0ZWQgY29sdW1ucyBhcyBlYWNoIGF0dHJpYnV0ZSBpcyB1bmlxdWUuIAoKIyMjIyBEdXBsaWNhdGUgcm93cyAKCmBgYHtyfQpzdW0oZHVwbGljYXRlZChkYXRhc2V0KSkKYGBgClRoZXJlIGlzIG9uZSBkdXBsaWNhdGVkIHJvdyBpbiB0aGUgZGF0YXNldCB0aGF0IHdlIG11c3QgZWxpbWluYXRlLiAKCgpgYGB7cn0KZHVwbGljYXRlZChkYXRhc2V0KQpgYGAKVGhlIGR1cGxpY2F0ZWQgcm93IGlzIGluIHJvdyA2NTMuCgpgYGB7cn0KZGF0YXNldFs2NTMsIF0KYGBgCkZyb20gdGhlIHRhYmxlIHdlIGNhbiBmaW5kIHRoZSBjb21wYW55IG5hbWUgb2YgdGhlIGR1cGxpY2F0ZSByb3cgdG8gdmVyaWZ5IGl0cyBkdXBsaWNhdGlvbi4gCgpgYGB7cn0KcmVkd29vZF9zeXN0ZW1zX3Jvd3MgPC0gZGF0YXNldFtkYXRhc2V0JG5hbWUgPT0gIlJlZHdvb2QgU3lzdGVtcyIsIF0KcHJpbnQocmVkd29vZF9zeXN0ZW1zX3Jvd3MpCmBgYApUaGVyZSBhcmUgdHdvIHJvd3MgZGVkaWNhdGVkIHRvICJSZWR3b29kIFN5c3RlbXMiLiBUaGlzIHNob3dzIHRoYXQgcm93IDY1MyBpcyBpbmRlZWQgYSBkdXBsaWNhdGUuIFRoZXJlZm9yZSwgd2UgbXVzdCBlbGltaW5hdGUgaXQuIAoKIyMjIyMgUmVtb3ZlIER1cGxpY2F0ZWQgUm93czoKYGBge3J9CmRhdGFzZXQgPC0gdW5pcXVlKGRhdGFzZXQpCmBgYApUaGlzIGNvZGUgY2h1bmsgcmVtb3ZlcyBkdXBsaWNhdGVkIHJvd3MuIAoKIyMjIyMgVmVyaWZ5IHRoZSBFbGltaW5hdGlvbiBvZiAgRHVwbGljYXRlczoKYGBge3J9CnN1bShkdXBsaWNhdGVkKGRhdGFzZXQpKQpgYGAKVGhlIGR1cGxpY2F0ZWQgcm93IGlzIHJlbW92ZWQuPGJyPgoKCiMjIyBCKSBDaGktc3F1YXJlZCBUZXN0CgpUaGUgY2hpLXNxdWFyZSB0ZXN0IGFpZHMgaW4gZmVhdHVyZSBzZWxlY3Rpb24gYnkgZXhhbWluaW5nIHRoZSBpbmRlcGVuZGVuY2UgYmV0d2VlbiBjYXRlZ29yaWNhbCBhdHRyaWJ1dGVzLiBJdCBoZWxwcyBlbGltaW5hdGUgYXR0cmlidXRlcyB0aGF0IHNob3djYXNlIGEgc2lnbmlmaWNhbnQgcmVsYXRpb25zaGlwIGJldHdlZW4gb25lIGFub3RoZXIuIAoKIyMjIyBSZWxhdGlvbnNoaXAgQmV0d2VlbiBsYWJlbHMgYW5kIHN0YXR1cyAKYGBge3J9CmNvbnRpbmdlbmN5X3RhYmxlIDwtIHRhYmxlKGRhdGFzZXQkbGFiZWxzLCBkYXRhc2V0JHN0YXR1cykKY2hpX3NxdWFyZV9yZXN1bHQgPC0gY2hpc3EudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoY2hpX3NxdWFyZV9yZXN1bHQpCmBgYApUaGUgcGVyZm9ybWVkIENoaS1zcXVhcmVkIHRlc3QgZGVtb25zdHJhdGVzIGEgc3Vic3RhbnRpYWwgYXNzb2NpYXRpb24gYmV0d2VlbiB0aGUg4oCYbGFiZWxz4oCZIGFuZCDigJhzdGF0dXPigJkgY29sdW1ucy4gVGhlIG9idGFpbmVkIHAtdmFsdWUsIHdoaWNoIGlzIGxlc3MgdGhhbiAyZS0xNiwgc3VnZ2VzdHMgYSAqKnN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgY29ycmVsYXRpb24qKi4gU3RhdHVzIGFuZCBsYWJlbHMgYXJlICoqZGVwZW5kZW50IG9uIG9uZSBhbm90aGVyLiBTbywgd2UgY2FuIGVsaW1pbmF0ZSBvbmUgb2YgdGhlbSoqLiBXZSBjaG9vc2UgdG8gZWxpbWluYXRlIGxhYmVscyBhbmQga2VlcCBzdGF0dXMgd2hpY2ggaXMgdGhlIGNsYXNzIGxhYmVsLiBMYWJlbHMgbWlnaHQgYmUgdGhlIGVuY29kZWQgdmVyc2lvbiBvZiBzdGF0dXMuIDxicj4KCiMjIyMgUmVsYXRpb25zaGlwIEJldHdlZW4gemlwX2NvZGUgYW5kIGNpdHkgCmBgYHtyfQpjb250aW5nZW5jeV90YWJsZSA8LSB0YWJsZShkYXRhc2V0JHppcF9jb2RlLCBkYXRhc2V0JGNpdHkpCmNoaV9zcXVhcmVfcmVzdWx0IDwtIGNoaXNxLnRlc3QoY29udGluZ2VuY3lfdGFibGUpCnByaW50KGNoaV9zcXVhcmVfcmVzdWx0KQpgYGAKVGhpcyBjb2RlIGNodW5rIGNvbmR1Y3RzIHRoZSBjaGktc3F1YXJlZCB0ZXN0IGZvciBhdHRyaWJ1dGVzIHppcF9jb2RlIGFuZCBjaXR5LiBXaXRoIHRoZSByZXN1bHRzIHNob3duLCB3ZSBjYW4gaW50ZXJwcmV0IHRoYXQgemlwX2NvZGUgYW5kIGNpdHkgaGF2ZSBhICoqc2lnbmlmaWNhbnQgY29ycmVsYXRpb24qKiBiZXR3ZWVuIHRoZW0uIEluIG90aGVyIHdvcmRzLCAqKnRoZXkgYXJlIGRlcGVuZGVudCBvbiBvbmUgYW5vdGhlci4gU28sIHdlIGNhbiBlbGltaW5hdGUgb25lIG9mIHRoZW0qKi4gV2UgY2hvc2UgdG8gZWxpbWluYXRlIHppcF9jb2RlIGFuZCBrZWVwIGNpdHkuIEJlY2F1c2UgY2l0eSBjb25zdGl0dXRlcyBhIHBhcnQgb2YgdGhlIGF0dHJpYnV0ZSBVbm5hbW1lZDogNiwgYW5kIGlmIHdlIHdhbnQgdG8gZGVsZXRlIGNvbHVtbiBVbm5hbWVkOiA2IHdlIG11c3Qga2VlcCBjaXR5Ljxicj4KCgojIyMjIEMpIEVsaW1pbmF0aW9uIG9mIFJlZHVuZGFudCBBdHRyaWJ1dGVzCgojIyMjIyBCZWZvcmUgQXR0cmlidXRlIEVsaW1pbmF0aW9uOgoKYGBge3J9Cm5jb2woZGF0YXNldCkKYGBgClRoZXJlIGFyZSA0OSBjb2x1bW5zIGluIHRoZSBkYXRhc2V0LiAKCmBgYHtyfQpuYW1lcyhkYXRhc2V0KQpgYGAKVW5uYW1lZDogMCwgbGF0aXR1ZGUsIGxvbmdpdHVkZSwgemlwX2NvZGUsIHN0YXRlX2NvZGUuMSwgaWQsIFVubmFtZWQ6IDYsIGFuZCBvYmplY3RfaWQgYXJlIGV4aXN0aW5nIGF0dHJpYnV0ZXMgaW4gdGhlIGRhdGFzZXQuIAoKVGhlIGNoaS1zcXVhcmVkIHRlc3Qgc2hvd2VkIHVzIHRoYXQgemlwX2NvZGUgYW5kIGNpdHkgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLiBBcyBhIHJlc3VsdCwgd2UgY2FuIGRlbGV0ZSBvbmUgb2YgdGhlbSBhbmQga2VlcCB0aGUgb3RoZXIuIAoKQXR0cmlidXRlcyBVbm5hbWVkOiAwLCBpZCwgVW5uYW1lZDogNiwgYW5kIG9iamVjdF9pZCBhcmUgaXJyZWxldmFudCB0byB0aGlzIGRhdGEgbWluaW5nIHRhc2suIEFzIGRpc2N1c3NlZCBpbiBzZWN0aW9uIDIsIHRoZSBvcmlnaW5hbCByZWZlcmVuY2UgcHJvdmlkZXMgbm8ganVzdGlmaWNhdGlvbiBvbiB0aGVpciB1c2FnZS4gVGhlcmVmb3JlLCB0aGV5IGFyZSB1bmltcG9ydGFudCBhbmQgY2FuIGJlIGVsaW1pbmF0ZWQuIAoKTGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBhdHRyaWJ1dGVzIGFyZSAiYWNjZXNzb3J5IiBhdHRyaWJ1dGVzIHdpdGggbm8gcmVhbCBpbXBhY3Qgb24gdGhlIGRhdGFzZXQuIERlbGV0aW5nIHRoZW0gd2lsbCBzaW1wbGlmeSB0aGUgbGF0ZXIgc3RlcHMuCgpTdGF0ZV9jb2RlIGFuZCBzdGF0ZV9jb2RlLjEgYXJlIGR1cGxpY2F0ZXMuIE9ubHkgb25lIGhhcyB0byByZW1haW4gdG8gcHJldmVudCByZWR1bmRhbmN5LiAKCgp8IEF0dHJpYnV0ZShzKSAgICAgICAgICAgICAgICAgIHwgS2VlcCAgICAgICAgICAgICAgIHwgUmVtb3ZlICAgICB8IFdoeSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwKfCBVbm5hbWVkOiAwICAgICAgICAgICAgICAgICAgICB8IGRvbid0IGtlZXAgICAgICAgICB8IFVubmFtZWQ6IDAgfCBJcnJlbGV2YW50ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgIAp8IHN0YXRlX2NvZGUsIHN0YXRlX2NvZGUuMSAgICAgIHwgc3RhdGVfY29kZSAgICAgICAgIHwgc3RhdGVfY29kZS4xIHwgRHVwbGljYXRlIGF0dHJpYnV0ZSAgICAgICAgICAgICAgICAgICB8CnwgbGF0aXR1ZGUgICAgICAgICAgICAgICAgICAgICAgfCBkb24ndCBrZWVwICAgICAgICAgfCBsYXRpdHVkZSAgIHwgVW5pbXBvcnRhbnQgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBsb25naXR1ZGUgICAgICAgICAgICAgICAgICAgICB8IGRvbid0IGtlZXAgICAgICAgICB8IGxvbmdpdHVkZSAgfCBVbmltcG9ydGFudCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IHppcF9jb2RlLCBjaXR5ICAgICAgICAgICAgICAgIHwgY2l0eSAgICAgICAgICAgICAgIHwgemlwX2NvZGUgICB8IERlcGVuZGVudCBhdHRyaWJ1dGVzIChjaGktc3F1YXJlZCB0ZXN0KSB8CnwgaWQgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBkb24ndCBrZWVwICAgICAgICAgfCBpZCAgICAgICAgIHwgSXJyZWxldmFudCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBsYWJlbHMsIHN0YXR1cyAgICAgICAgICAgICAgICB8IHN0YXR1cyAgICAgICAgICAgICB8IGxhYmVscyAgICAgfCBEZXBlbmRlbnQgYXR0cmlidXRlcyAoY2hpLXNxdWFyZWQgdGVzdCkgfAp8IFVubmFtZWQ6IDYsIGNpdHksIHN0YXRlX2NvZGUuMXwgY2l0eSwgc3RhdGVfY29kZS4xIHwgVW5uYW1lZDo2ICB8IFJlZHVuZGFudCBhdHRyaWJ1dGUgICAgICAgICAgICAgICAgICAgICB8Cnwgb2JqZWN0X2lkICAgICAgICAgICAgICAgICAgICAgfCBkb24ndCBrZWVwICAgICAgICAgfCBvYmplY3RfaWQgIHwgSXJyZWxldmFudCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKCiMjIyMjIFJlZHVuZGFudCBBdHRyaWJ1dGUgRWxpbWluYXRpb246CmBgYHtyfQojIENyZWF0ZSBhIGxpc3Qgb2YgY29sdW1uIG5hbWVzIHRvIHJlbW92ZQpjb2x1bW5zX3RvX3JlbW92ZSA8LSBjKCJVbm5hbWVkOiAwIiwgInN0YXRlX2NvZGUuMSIsICJsYXRpdHVkZSIsICJsb25naXR1ZGUiLCAiemlwX2NvZGUiLCAiaWQiLCAiVW5uYW1lZDogNiIsICJsYWJlbHMiLCAib2JqZWN0X2lkIikKCiMgUmVtb3ZlIHRoZSBzcGVjaWZpZWQgY29sdW1ucyBmcm9tIHRoZSBkYXRhc2V0CmRhdGFzZXQgPC0gZGF0YXNldFssICFuYW1lcyhkYXRhc2V0KSAlaW4lIGNvbHVtbnNfdG9fcmVtb3ZlXQpgYGAKVGhpcyBjb2RlIGNodW5rIHJlbW92ZXMgYWxsIGF0dHJpYnV0ZXMgcmVkdW5kYW50LCBpcnJlbGV2YW50LCBhbmQgdW5pbXBvcnRhbnQgZnJvbSB0aGUgZGF0YXNldC4KCiMjIyMjIEFmdGVyIEF0dHJpYnV0ZSBFbGltaW5hdGlvbjoKCmBgYHtyfQpuY29sKGRhdGFzZXQpCmBgYApUaGVyZSBhcmUgNDAgY29sdW1ucyBpbiB0aGUgZGF0YXNldC4gCgpgYGB7cn0KbmFtZXMoZGF0YXNldCkKYGBgClVubmFtZWQ6IDAsIGxhdGl0dWRlLCBsb25naXR1ZGUsIHppcF9jb2RlLCBzdGF0ZV9jb2RlLjEsIGlkLCBVbm5hbWVkOiA2LCBhbmQgb2JqZWN0X2lkIGRvIG5vdCBleGlzdCBpbiB0aGUgZGF0YXNldC4gPGJyPjxicj4KCgojIyA0LjQtRGF0YSBUcmFuc2Zvcm1hdGlvbgoKSW4gZGF0YSB0cmFuc2Zvcm1hdGlvbiwgd2UgYXJlIHByZXBhcmluZyBkYXRhIGZvciBhbmFseXNpcyBhbmQgbW9kZWxpbmcgdGhyb3VnaCBmbG9vcmluZywgbm9ybWFsaXphdGlvbiwgYW5kIGVuY29kaW5nIHRlY2huaXF1ZXMuIDxicj4KCiMjIyMgQSkgRmxvb3JpbmcgQXR0cmlidXRlcyAKCkZsb29yaW5nIGRhdGEsIGFuIGVzc2VudGlhbCBhc3BlY3Qgb2YgZGF0YSB0cmFuc2Zvcm1hdGlvbiwgaXMgY3J1Y2lhbCBmb3IgdmFyaW91cyBhbmFseXRpY2FsIGFuZCBtb2RlbGluZyBwcm9jZXNzZXMuIEl0IGZhY2lsaXRhdGVzIHRoZSBjb252ZXJzaW9uIG9mIGNvbnRpbnVvdXMgbnVtZXJpY2FsIGF0dHJpYnV0ZXMgaW50byBkaXNjcmV0ZSB2YWx1ZXMgYnkgcm91bmRpbmcgZG93biB0byB0aGUgbmVhcmVzdCB3aG9sZSBudW1iZXIuIFRoaXMgdGVjaG5pcXVlIGlzIHZpdGFsIGluIHNpbXBsaWZ5aW5nIGNvbXBsZXggbnVtZXJpY2FsIGRhdGEsIG1ha2luZyBpdCBtb3JlIG1hbmFnZWFibGUgYW5kIGVhc2llciB0byBpbnRlcnByZXQuCgojIyMjIyBBdHRyaWJ1dGVzIEJlZm9yZSBGbG9vcmluZzoKYGBge3J9CiMgU2VsZWN0aW5nIHRoZSBzcGVjaWZpYyBjb2x1bW5zIGZvciB0aGUgZmlyc3Qgcm93CmZpcnN0X3JvdyA8LSBkYXRhc2V0WzEsIGMoImFnZV9maXJzdF9mdW5kaW5nX3llYXIiLCAiYWdlX2xhc3RfZnVuZGluZ195ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyIiwgImFnZV9sYXN0X21pbGVzdG9uZV95ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAicmVsYXRpb25zaGlwcyIsICJmdW5kaW5nX3JvdW5kcyIsICJmdW5kaW5nX3RvdGFsX3VzZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIm1pbGVzdG9uZXMiLCAiYXZnX3BhcnRpY2lwYW50cyIpXQojIFByaW50aW5nIHRoZSBmaXJzdCByb3cKcHJpbnQoZmlyc3Rfcm93KQpgYGAKQXMgc2hvd24gYWJvdmUsIHRoZSBhdHRyaWJ1dGVzIGNvbnRhaW4gY29udGludW91cyB2YWx1ZXMgYmVmb3JlIGZsb29yaW5nLiAKCiMjIyMjIEZsb29yaW5nOiAKYGBge3J9CiMgQ29sdW1ucyB0byBmbG9vcgpjb2xzX3RvX2Zsb29yIDwtIGMoImFnZV9maXJzdF9mdW5kaW5nX3llYXIiLCAiYWdlX2xhc3RfZnVuZGluZ195ZWFyIiwKICAgICAgICAgICAgICAgICAgICJhZ2VfZmlyc3RfbWlsZXN0b25lX3llYXIiLCAiYWdlX2xhc3RfbWlsZXN0b25lX3llYXIiLAogICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcHMiLCAiZnVuZGluZ19yb3VuZHMiLCAiZnVuZGluZ190b3RhbF91c2QiLAogICAgICAgICAgICAgICAgICAgIm1pbGVzdG9uZXMiLCAiYXZnX3BhcnRpY2lwYW50cyIpCgojIEFwcGx5aW5nIGZsb29yIHRvIHNwZWNpZmllZCBjb2x1bW5zCmRhdGFzZXRbY29sc190b19mbG9vcl0gPC0gbGFwcGx5KGRhdGFzZXRbY29sc190b19mbG9vcl0sIGZsb29yKQpgYGAKVGhpcyBjb2RlIGNodW5rIGZsb29ycyBhdHRyaWJ1dGVzIGZyb20gY29udGludW91cyB0byBkaXNjcmV0ZSBudW1iZXJzLiAKCiMjIyMjIEF0dHJpYnV0ZXMgQWZ0ZXIgRmxvb3Jpbmc6CmBgYHtyfQojIFNlbGVjdGluZyB0aGUgc3BlY2lmaWMgY29sdW1ucyBmb3IgdGhlIGZpcnN0IHJvdwpmaXJzdF9yb3cgPC0gZGF0YXNldFsxLCBjKCJhZ2VfZmlyc3RfZnVuZGluZ195ZWFyIiwgImFnZV9sYXN0X2Z1bmRpbmdfeWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgImFnZV9maXJzdF9taWxlc3RvbmVfeWVhciIsICJhZ2VfbGFzdF9taWxlc3RvbmVfeWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcHMiLCAiZnVuZGluZ19yb3VuZHMiLCAiZnVuZGluZ190b3RhbF91c2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJtaWxlc3RvbmVzIiwgImF2Z19wYXJ0aWNpcGFudHMiKV0KIyBQcmludGluZyB0aGUgZmlyc3Qgcm93CnByaW50KGZpcnN0X3JvdykKYGBgCkFmdGVyIGZsb29yaW5nLCB0aGUgYXR0cmlidXRlcyBhcmUgbm93IGRpc2NyZXRlIGluc3RlYWQgb2YgY29udGludW91cy48YnI+PGJyPgoKCiMjIyMgQikgTm9ybWFsaXphdGlvbiAKCldlIGFyZSBnb2luZyB0byBub3JtYWxpemUgdGhlIG51bWVyaWNhbCBhdHRyaWJ1dGUgZnVuZGluZ190b3RhbF91c2QgdXNpbmcgbWluLW1heCBub3JtYWxpemF0aW9uLiBOdW1iZXJzIHNob3VsZCBmYWxsIGJldHdlZW4gMCBhbmQgMSAoaW5jbHVzaXZlKS48YnI+IAoKIyMjIyMgZnVuZGluZ190b3RhbF91c2QgQmVmb3JlIE5vcm1hbGl6YXRpb246IApgYGB7cn0KIyBTZWxlY3RpbmcgdGhlIHNwZWNpZmljIGNvbHVtbnMgZm9yIHRoZSBmaXJzdCByb3cKZmlyc3Rfcm93IDwtIGRhdGFzZXRbMSwgYygiZnVuZGluZ190b3RhbF91c2QiKV0KIyBQcmludGluZyB0aGUgZmlyc3Qgcm93CnByaW50KGZpcnN0X3JvdykKYGBgClRoZSB0YWJsZSBhYm92ZSBzaG93cyBhbiB1bm5vcm1hbGl6ZWQgdmFsdWUgZnJvbSB0aGUgZnVuZGluZ190b3RhbF91c2QgYXR0cmlidXRlLiA8YnI+CgojIyMjIyBOb3JtYWxpemF0aW9uOiAKYGBge3J9Cm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4KSB7cmV0dXJuICgoeCAtIG1pbih4KSkgLyAobWF4KHgpIC0gbWluKHgpKSl9CmRhdGFzZXQkZnVuZGluZ190b3RhbF91c2Q8LW5vcm1hbGl6ZShkYXRhc2V0JGZ1bmRpbmdfdG90YWxfdXNkKQpgYGAKVGhpcyBjb2RlIGNodW5rIG5vcm1hbGl6ZXMgYXR0cmlidXRlIGZ1bmRpbmdfdG90YWxfdXNkIHVzaW5nIG1pbi1tYXggbm9ybWFsaXphdGlvbi4gPGJyPgoKIyMjIyMgZnVuZGluZ190b3RhbF91c2QgQWZ0ZXIgTm9ybWFsaXphdGlvbjoKYGBge3J9CiMgU2VsZWN0aW5nIHRoZSBzcGVjaWZpYyBjb2x1bW5zIGZvciB0aGUgZmlyc3Qgcm93CmZpcnN0X3JvdyA8LSBkYXRhc2V0WzEsIGMoImZ1bmRpbmdfdG90YWxfdXNkIildCiMgUHJpbnRpbmcgdGhlIGZpcnN0IHJvdwpwcmludChmaXJzdF9yb3cpCmBgYApUaGUgdGFibGUgYWJvdmUgc2hvd3MgYSBub3JtYWxpemVkIHZhbHVlIGZyb20gdGhlIGZ1bmRpbmdfdG90YWxfdXNkIGF0dHJpYnV0ZS4gCgpgYGB7cn0KbWluX3ZhbHVlIDwtIG1pbihkYXRhc2V0JGZ1bmRpbmdfdG90YWxfdXNkKQptYXhfdmFsdWUgPC0gbWF4KGRhdGFzZXQkZnVuZGluZ190b3RhbF91c2QpCgojIFByaW50IHRoZSByZXN1bHRzIHdpdGggbGFiZWxzCmNhdCgiVGhlIG1pbiBpczoiLCBtaW5fdmFsdWUsICJcbiIpCmNhdCgiVGhlIG1heCBpczoiLCBtYXhfdmFsdWUpCmBgYApJbiB0aGUgbWluLW1heCBub3JtYWxpemF0aW9uIG9mIGF0dHJpYnV0ZSBmdW5kaW5nX3RvdGFsX3VzZCwgdGhlIG1pbmltdW0gaXMgMCB3aGlsZSB0aGUgbWF4IGlzIDEuIAoKYGBge3J9CiMgRmluZCByb3cgaW5kZXggZm9yIG1pbmltdW0gYW5kIG1heGltdW0gZnVuZGluZ190b3RhbF91c2QKbWluX3JvdyA8LSB3aGljaC5taW4oZGF0YXNldCRmdW5kaW5nX3RvdGFsX3VzZCkKbWF4X3JvdyA8LSB3aGljaC5tYXgoZGF0YXNldCRmdW5kaW5nX3RvdGFsX3VzZCkKCiMgUHJpbnQgcm93cyB3aXRoIG1pbmltdW0gYW5kIG1heGltdW0gZnVuZGluZ190b3RhbF91c2QgYWxvbmcgd2l0aCBuYW1lIGFuZCBzdGF0dXMKcHJpbnQoZGF0YXNldFttaW5fcm93LCBjKCduYW1lJywgJ2Z1bmRpbmdfdG90YWxfdXNkJywgJ3N0YXR1cycpXSkKCnByaW50KGRhdGFzZXRbbWF4X3JvdywgYygnbmFtZScsICdmdW5kaW5nX3RvdGFsX3VzZCcsICdzdGF0dXMnKV0pCmBgYApJbiBib3RoIHRhYmxlcywgd2UgY2FuIHZlcmlmeSB0aGF0IHRoZSBtaW4tbWF4IG5vcm1hbGl6YXRpb24gd2FzIHN1Y2Nlc3NmdWwuIFRoZSBmaXJzdCB0YWJsZSBzaG93cyB0aGUgcm93IHdpdGggbWluaW11bSBub3JtYWxpemF0aW9uIGFuZCB0aGUgc2Vjb25kIHRhYmxlIHNob3dzIHRoZSByb3cgd2l0aCBtYXhpbXVtIG5vcm1hbGl6YXRpb24uPGJyPjxicj4KCiMjIyMgQykgRW5jb2RpbmcgCgpIZXJlLCB3ZSB3aWxsIGVuY29kZSBhdHRyaWJ1dGVzIHRvIHNpbXBsaWZ5IGFuYWx5c2lzLiBUaGUgYXR0cmlidXRlcyB0byBlbmNvZGUgYXJlOgoKaSkgRGF0ZSBhdHRyaWJ1dGVzOiBmb3VuZGVkX2F0LCBjbG9zZWRfYXQsIGZpcnN0X2Z1bmRpbmdfYXQsIGxhc3RfZnVuZGluZ19hdAoKaWkpIENsYXNzIGxhYmVsOiBzdGF0dXMKCmlpaSkgQ2F0ZWdvcmljYWwgYXR0cmlidXRlczogc3RhdGVfY29kZSwgY2F0ZWdvcnlfY29kZSwgYW5kIGNpdHkKCml2KSBVbmlxdWUgYXR0cmlidXRlOiBuYW1lCgoKIyMjIyMgQXR0cmlidXRlcyBCZWZvcmUgRW5jb2Rpbmc6CmBgYHtyfQpkYXRhc2V0WzMsIGMoInN0YXRlX2NvZGUiLCAiY2l0eSIsICJuYW1lIiwgImZvdW5kZWRfYXQiLCAiY2xvc2VkX2F0IiwgImZpcnN0X2Z1bmRpbmdfYXQiLCAibGFzdF9mdW5kaW5nX2F0IiwgImNhdGVnb3J5X2NvZGUiLCAic3RhdHVzIildCmBgYApBdHRyaWJ1dGVzIGFwcGVhcmluZyBpbiB0aGVpciBvcmlnaW5hbCBmb3JtYXQuIAoKCiMjIyBpKSBFbmNvZGluZyBEYXRlIEF0dHJpYnV0ZXM6IGZvdW5kZWRfYXQsIGNsb3NlZF9hdCwgZmlyc3RfZnVuZGluZ19hdCwgbGFzdF9mdW5kaW5nX2F0CmBgYHtyfQpkYXRhc2V0JGZvdW5kZWRfYXQgPC0gZ3N1YigiLyIsICIiLCBkYXRhc2V0JGZvdW5kZWRfYXQpCmRhdGFzZXQkY2xvc2VkX2F0IDwtIGdzdWIoIi8iLCAiIiwgZGF0YXNldCRjbG9zZWRfYXQpCmRhdGFzZXQkZmlyc3RfZnVuZGluZ19hdCA8LSBnc3ViKCIvIiwgIiIsIGRhdGFzZXQkZmlyc3RfZnVuZGluZ19hdCkKZGF0YXNldCRsYXN0X2Z1bmRpbmdfYXQgPC0gZ3N1YigiLyIsICIiLCBkYXRhc2V0JGxhc3RfZnVuZGluZ19hdCkKCmRhdGFzZXQkZm91bmRlZF9hdCA8LSBzdWJzdHIoZGF0YXNldCRmb3VuZGVkX2F0LCBuY2hhcihkYXRhc2V0JGZvdW5kZWRfYXQpIC0gMywgbmNoYXIoZGF0YXNldCRmb3VuZGVkX2F0KSkKZGF0YXNldCRjbG9zZWRfYXQgPC0gc3Vic3RyKGRhdGFzZXQkY2xvc2VkX2F0LCBuY2hhcihkYXRhc2V0JGNsb3NlZF9hdCkgLSAzLCBuY2hhcihkYXRhc2V0JGNsb3NlZF9hdCkpCmRhdGFzZXQkZmlyc3RfZnVuZGluZ19hdCA8LSBzdWJzdHIoZGF0YXNldCRmaXJzdF9mdW5kaW5nX2F0LCBuY2hhcihkYXRhc2V0JGZpcnN0X2Z1bmRpbmdfYXQpIC0gMywgbmNoYXIoZGF0YXNldCRmaXJzdF9mdW5kaW5nX2F0KSkKZGF0YXNldCRsYXN0X2Z1bmRpbmdfYXQgPC0gc3Vic3RyKGRhdGFzZXQkbGFzdF9mdW5kaW5nX2F0LCBuY2hhcihkYXRhc2V0JGxhc3RfZnVuZGluZ19hdCkgLSAzLCBuY2hhcihkYXRhc2V0JGxhc3RfZnVuZGluZ19hdCkpCgpkYXRhc2V0JGZvdW5kZWRfYXQgPC0gYXMubnVtZXJpYyhkYXRhc2V0JGZvdW5kZWRfYXQpCmRhdGFzZXQkY2xvc2VkX2F0IDwtIGFzLm51bWVyaWMoZGF0YXNldCRjbG9zZWRfYXQpCmRhdGFzZXQkZmlyc3RfZnVuZGluZ19hdCA8LSBhcy5udW1lcmljKGRhdGFzZXQkZmlyc3RfZnVuZGluZ19hdCkKZGF0YXNldCRsYXN0X2Z1bmRpbmdfYXQgPC0gYXMubnVtZXJpYyhkYXRhc2V0JGxhc3RfZnVuZGluZ19hdCkKYGBgClRoaXMgY29kZSBjaHVuayBjb252ZXJ0cyBhdHRyaWJ1dGVzIGZvdW5kZWRfYXQsIGNsb3NlZF9hdCwgZmlyc3RfZnVuZGluZ19hdCwgbGFzdF9mdW5kaW5nX2F0IGZyb20gZGF0ZSBmb3JtYXQgbW0vZGQveXl5eSB0byBudW1lcmljYWwgbnVtYmVycy4gCgoKIyMjIGlpKSBFbmNvZGluZyBDYXRlZ29yaWNhbCBBdHRyaWJ1dGVzOiBzdGF0ZV9jb2RlLCBjYXRlZ29yeV9jb2RlLCBhbmQgY2l0eSAKYGBge3J9CmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkobGF0dGljZSkKCiMgQ29sdW1ucyB0byBiZSBlbmNvZGVkCmF0dHJpYnV0ZXNfdG9fZW5jb2RlIDwtIGMoInN0YXRlX2NvZGUiLCAiY2F0ZWdvcnlfY29kZSIsICJjaXR5IikKCiMgTG9vcCB0aHJvdWdoIGVhY2ggYXR0cmlidXRlIGZvciBlbmNvZGluZwpmb3IgKGF0dHJpYnV0ZSBpbiBhdHRyaWJ1dGVzX3RvX2VuY29kZSkgewogICMgVXNlIGNhcmV0J3MgbWV0aG9kIGZvciBlbmNvZGluZwogIGRhdGFzZXRbW2F0dHJpYnV0ZV1dIDwtIGFzLmZhY3RvcihkYXRhc2V0W1thdHRyaWJ1dGVdXSkKICBkYXRhc2V0W1thdHRyaWJ1dGVdXSA8LSBhcy5udW1lcmljKGRhdGFzZXRbW2F0dHJpYnV0ZV1dKQp9CmBgYAoKVGhpcyBjb2RlIGNodW5rIGNyZWF0ZXMgZnJvbSBlYWNoIG9mIHRoZSBzZWxlY3RlZCBjb2x1bW5zIGEgbmV3IGNvbHVtbiB0aGF0IGhvbGRzIGEgZnJlcXVlbmN5LWVuY29kZWQgdmVyc2lvbiBvZiB0aGUgYXR0cmlidXRlcyB2YWx1ZXMuIFJvd3MgdGhhdCBzaGFyZSB0aGUgc2FtZSB2YWx1ZSBvZiB0aGUgYXR0cmlidXRlIGhhdmUgdGhlIHNhbWUgZW5jb2RpbmcuIAoKCiMjIyBpaWkpIEVjb2RpbmcgVW5pcXVlIEF0dHJpYnV0ZTogbmFtZQpgYGB7cn0KIyBSZXBsYWNlIGVhY2ggdW5pcXVlIGNhdGVnb3J5IHdpdGggYSBudW1lcmljYWwgbGFiZWwKZGF0YXNldCRuYW1lIDwtIGFzLm51bWVyaWMoZmFjdG9yKGRhdGFzZXQkbmFtZSwgbGV2ZWxzID0gdW5pcXVlKGRhdGFzZXQkbmFtZSkpKQpgYGAKQ3JlYXRlcyBhIG5ldyBjb2x1bW4gZm9yIHRoZSBlbmNvZGVkIHVuaXF1ZSB2YWx1ZXMgb2YgdGhlIGF0dHJpYnV0ZSBuYW1lLiAKCgojIyMgaXYpIEVuY29kaW5nIENsYXNzIExhYmVsOiBzdGF0dXMKYGBge3J9CmRhdGFzZXQkc3RhdHVzIDwtIGlmZWxzZShkYXRhc2V0JHN0YXR1cyA9PSAiYWNxdWlyZWQiLCAxLCAwKQpgYGAKRW5jb2RlcyBzdGF0dXMgYXR0cmlidXRlIHRvIDEgZm9yIGFjcXVpcmVkIHN0YXR1cyBhbmQgMCBmb3IgY2xvc2VkIHN0YXR1cy4gCgoKIyMjIyMgQXR0cmlidXRlcyBBZnRlciBFbmNvZGluZzoKYGBge3J9CmRhdGFzZXRbMywgYygic3RhdGVfY29kZSIsICJjaXR5IiwgIm5hbWUiLCAiZm91bmRlZF9hdCIsICJjbG9zZWRfYXQiLCAiZmlyc3RfZnVuZGluZ19hdCIsICJsYXN0X2Z1bmRpbmdfYXQiLCAiY2F0ZWdvcnlfY29kZSIsICJzdGF0dXMiKV0KYGBgCkF0dHJpYnV0ZXMgZm91bmRlZF9hdCwgY2xvc2VkX2F0LCBmaXJzdF9mdW5kaW5nX2F0LCBhbmQgbGFzdF9mdW5kaW5nX2F0IGFwcGVhciBpbiB0aGUgZW5jb2RlZCAoIG1tLXl5eXkgKSBmb3JtLiBBdHRyaWJ1dGUgc3RhdHVzIGFwcGVhciBpbiAxIChmb3IgYWNxdWlyZWQpIGFuZCAwIChmb3IgY2xvc2VkKSBmb3JtLiAKCiMjIyBEb3dubG9hZGluZyB0aGUgUHJlLXByb2Nlc3NlZCBEYXRhc2V0IAoKYGBge3J9CmxpYnJhcnkod3JpdGV4bCkKCiMgQXNzdW1pbmcgeW91ciBwcmVwcm9jZXNzZWQgZGF0YXNldCBpcyBuYW1lZCAnZGF0YXNldCcKd3JpdGVfeGxzeChkYXRhc2V0LCBwYXRoID0gIlByZXByb2Nlc3NlZF9TdGFydHVwRGF0YS54bHN4IikKcHJlcHJvY2Vzc2VkX2RhdGFzZXQgPC0gcmVhZF9leGNlbCgiUHJlcHJvY2Vzc2VkX1N0YXJ0dXBEYXRhLnhsc3giKQpgYGAKVG8gc2F2ZSB0aGUgcHJlLXByb2Nlc3Npbmcgd29yayBhbmQgdXNlIGl0IGxhdGVyIGluIGNsYXNzaWZpY2F0aW9uIGFuZCBjbHVzdGVyaW5nLiAKCiMjIENvbXBhcmlzb24gb2YgQXR0cmlidXRlcyBCZWZvcmUgdnMgQWZ0ZXIgUHJlLXByb2Nlc3NpbmcKCkFmdGVyIHByZXByb2Nlc3NpbmcsIHRoZSBkYXRhIHVuZGVyZ29lcyB0cmFuc2Zvcm1hdGlvbiB0aGF0IGNhbiBpbmNsdWRlIGNsZWFuaW5nLCBub3JtYWxpemF0aW9uLCBlbmNvZGluZywgYW5kIG1vcmUsIHJlc3VsdGluZyBpbiBhIHJlZmluZWQgZGF0YXNldCBtb3JlIGFtZW5hYmxlIHRvIGFuYWx5c2lzLCByZWR1Y2luZyBub2lzZSwgYW5kIGVuaGFuY2luZyB0aGUgYWNjdXJhY3kgb2YgdGhlIGxlYXJuaW5nbW9kZWxzLgoKRmlyc3QsIHdlIHdpbGwgY2hlY2sgdGhlIG51bWJlciBvciBjb2x1bW5zIGFuZCBhdHRyaWJ1dGVzIGFmdGVyIHByZS1wcm9jZXNzaW5nLiAKCmBgYHtyfQojIE51bWJlciBvZiBjb2x1bW5zCm51bV9jb2xzIDwtIG5jb2woZGF0YXNldCkKIyBOdW1iZXIgb2YgYXR0cmlidXRlcwpudW1fYXR0cnMgPC0gbnJvdyhkYXRhc2V0KQoKIyBQcmludCB0aGUgdmFsdWVzCmNhdCgiTnVtYmVyIG9mIGNvbHVtbnM6IiwgbnVtX2NvbHMsICJcbiIpCmNhdCgiTnVtYmVyIG9mIGF0dHJpYnV0ZXM6IiwgbnVtX2F0dHJzLCAiXG4iKQpgYGAKVGhlcmUgYXJlIDQwIGNvbHVtbnMgYW5kIDcyNyByb3dzLiAKClRoZW4sIHdlIHdpbGwgb2JzZXJ2ZSBjaGFuZ2VzIG9jY3VycmVkIHRvIG51bWVyaWNhbCwgbm9taW5hbCwgYW5kIGJpbmFyeSBhdHRyaWJ1dGVzIGFmdGVyIHByZS1wcm9jZXNzaW5nLjxicj4KCiMjIyBBKSBBc3Nlc3NpbmcgQ2xhc3MgTGFiZWwgQmFsYW5jZSBBZnRlciBQcmUtcHJvY2Vzc2luZwoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShncmlkKQoKIyBDcmVhdGUgYSBiYXIgcGxvdCBmb3IgdGhlICJzdGF0dXMiIGF0dHJpYnV0ZQpnZyA8LSBnZ3Bsb3QoZGF0YXNldCwgYWVzKHggPSBzdGF0dXMpKSArCiAgZ2VvbV9iYXIoZmlsbCA9ICJkYXJrZ3JheSIsIGNvbG9yID0gImJsYWNrIikgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFN0YXJ0dXAgU3RhdHVzIiwgeCA9ICJTdGF0dXMiLCB5ID0gIkNvdW50IikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKIyBQcmludCB0aGUgcGxvdApwcmludChnZykKCiMgQWRkIGFuIGV4dGVybmFsIGFubm90YXRpb24gdG8gdGhlIHJpZ2h0IHNpZGUKZ3JpZC50ZXh0KCIxID0gQWNxdWlyZWRcbjAgPSBDbG9zZWQiLCB4ID0gMC4yLCB5ID0gMC45MiwganVzdCA9IGMoInJpZ2h0IiwgInRvcCIpLCBncCA9IGdwYXIoZm9udHNpemUgPSAxMiwgY29sID0gImJsYWNrIikpCmBgYApEZXNwaXRlIHByZS1wcm9jZXNzaW5nLCAqKnRoZXJlIGlzIHN0aWxsIGEgY2xhc3MgaW1iYWxhbmNlIGluIHRoZSBjbGFzcyBsYWJlbCAoc3RhdHVzKSoqLiA8YnI+CgoKIyMjIyBCKSBOdW1lcmljYWwgQXR0cmlidXRlcyBCZWZvcmUgdnMgQWZ0ZXIgUHJlLXByb2Nlc3NpbmcKCmBgYHtyfQojIExvYWQgdGhlIHJlcXVpcmVkIGxpYnJhcmllcwpsaWJyYXJ5KGdncGxvdDIpCgojIFNlbGVjdCBudW1lcmljYWwgYXR0cmlidXRlcyBmb3IgaGlzdG9ncmFtcwpudW1lcmljYWxfYXR0cmlidXRlcyA8LSBjKCJhZ2VfZmlyc3RfZnVuZGluZ195ZWFyIiwgImFnZV9sYXN0X2Z1bmRpbmdfeWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhZ2VfZmlyc3RfbWlsZXN0b25lX3llYXIiLCAiYWdlX2xhc3RfbWlsZXN0b25lX3llYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicmVsYXRpb25zaGlwcyIsICJmdW5kaW5nX3JvdW5kcyIsICJmdW5kaW5nX3RvdGFsX3VzZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJtaWxlc3RvbmVzIiwgImF2Z19wYXJ0aWNpcGFudHMiKQoKIyBNZWx0IHRoZSBkYXRhc2V0IGZvciBlYXNpZXIgcGxvdHRpbmcKbWVsdGVkX2RhdGEgPC0gcmVzaGFwZTI6Om1lbHQoZGF0YXNldFssIG51bWVyaWNhbF9hdHRyaWJ1dGVzXSkKCiMgQ3JlYXRlIGhpc3RvZ3JhbSB3aXRoIGZhY2V0IHdyYXAKaGlzdG9ncmFtX3Bsb3RfbnVtZXJpY2FsX2FmdGVyIDwtIGdncGxvdChtZWx0ZWRfZGF0YSwgYWVzKHggPSB2YWx1ZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGZpbGwgPSAiZGFya2dyYXkiLCBjb2xvciA9ICJibGFjayIpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArCiAgbGFicyh0aXRsZSA9ICJIaXN0b2dyYW0gZm9yIE51bWVyaWNhbCBBdHRyaWJ1dGVzIEFGVEVSIFByZS1wcm9jZXNzaW5nIiwgeCA9ICJWYWx1ZSIsIHkgPSAiRnJlcXVlbmN5IikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKIyBTZXQgdXAgYSAxeDIgcGxvdHRpbmcgZ3JpZCB0byBzaG93IGhpc3RvZ3JhbXMgc2lkZSBieSBzaWRlCnBhcihtZnJvdyA9IGMoMSwgMikpCgojIFBsb3QgdGhlIGhpc3RvZ3JhbXMgc2lkZSBieSBzaWRlCnBsb3QoaGlzdG9ncmFtX3Bsb3RfbnVtZXJpY2FsX2JlZm9yZSkKcGxvdChoaXN0b2dyYW1fcGxvdF9udW1lcmljYWxfYWZ0ZXIpCmBgYApUaGUgZ3JhcGhzIGFib3ZlIHNob3cgYSBjb21wYXJpc29uIGJldHdlZW4gbnVtZXJpY2FsIGRhdGEgQkVGT1JFIGFuZCBBRlRFUiBwcmUtcHJvY2Vzc2luZy4gTWFueSBhbHRlcmF0aW9ucyBoYXZlIHRha2VuIHBsYWNlIHdpdGhpbiB0aGUgbnVtZXJpY2FsIGF0dHJpYnV0ZXMuIEZyb20gZmxvb3JpbmcsIG5vcm1hbGl6YXRpb24sIGFuZCByZW1vdmluZyBvdXRsaWVycy4gPGJyPgoKIyMjIyBDKSBOb21pbmFsIEF0dHJpYnV0ZXMgQmVmb3JlIHZzIEFmdGVyIFByZS1wcm9jZXNzaW5nCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpZHlyKQoKIyBTZWxlY3Rpbmcgc3BlY2lmaWMgY29sdW1ucyBmb3IgdmlzdWFsaXphdGlvbgpjb2x1bW5zX3RvX3Zpc3VhbGl6ZSA8LSBkYXRhc2V0WywgYygic3RhdGVfY29kZSIsICJjaXR5IiwgImNhdGVnb3J5X2NvZGUiKV0KCiMgTWVsdCB0aGUgcmVxdWlyZWQgY29sdW1ucyBmb3IgdmlzdWFsaXphdGlvbgptZWx0ZWRfZGF0YV9iZWZvcmUgPC0gZ2F0aGVyKGRhdGEgPSBjb2x1bW5zX3RvX3Zpc3VhbGl6ZSkKCiMgUGxvdHRpbmcgYmFyIGdyYXBocyBmb3IgdGhlIHNwZWNpZmllZCBhdHRyaWJ1dGVzIGFuZCBmYWNldF93cmFwCmJhcnBsb3Rfbm9taW5hbF9hZnRlciA8LSBnZ3Bsb3QobWVsdGVkX2RhdGFfYmVmb3JlLCBhZXMoeCA9IHZhbHVlLCBmaWxsID0ga2V5KSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJjb3VudCIsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrZ3JheSIpICsKICBmYWNldF93cmFwKH5rZXksIHNjYWxlcyA9ICJmcmVlIikgKwogIGxhYnModGl0bGUgPSAiTm9taW5hbCBBdHRyaWJ1dGVzIEFGVEVSIFByZS1wcm9jZXNzaW5nIiwgeCA9ICJWYWx1ZXMiLCB5ID0gIkNvdW50IikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKIyBEaXNwbGF5aW5nIHRoZSBwbG90CnByaW50KGJhcnBsb3Rfbm9taW5hbF9iZWZvcmUpCnByaW50KGJhcnBsb3Rfbm9taW5hbF9hZnRlcikKYGBgCk1hbnkgYWx0ZXJhdGlvbnMgaGF2ZSB0YWtlbiBwbGFjZSB3aXRoaW4gdGhlIG5vbWluYWwgYXR0cmlidXRlcy4gQXR0cmlidXRlcyB3ZXJlIGVuY29kZWQgYW5kIG91dGxpZXJzIHdlcmUgcmVtb3ZlZC4gSW4gYWRkaXRpb24sIGF0dHJpYnV0ZSBzdGF0ZV9jb2RlLjEgd2FzIGRlZW1lZCByZWR1bmRhbnQgYW5kIGhlbmNlIHJlbW92ZWQuIEZyb20gdGhlIGdyYXBocywgd2UgY2FuIG9ic2VydmUgZnJlcXVlbnQgdHJlbmRzIGluIGNhdGVnb3J5X2NvZGUsIGNpdHksIGFuZCBzdGF0ZV9jb2RlLiA8YnI+CgoKIyMjIyBEKSBCaW5hcnkgQXR0cmlidXRlcyBCZWZvcmUgdnMgQWZ0ZXIgUHJlLXByb2Nlc3NpbmcKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXIpCgojIExpc3Qgb2YgYmluYXJ5IGF0dHJpYnV0ZXMKYmluYXJ5X2F0dHJpYnV0ZXMgPC0gYygiaXNfQ0EiLCAiaXNfTlkiLCAiaXNfTUEiLCAiaXNfVFgiLCAiaXNfb3RoZXJzdGF0ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICJpc19zb2Z0d2FyZSIsICJpc193ZWIiLCAiaXNfbW9iaWxlIiwgImlzX2VudGVycHJpc2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAiaXNfYWR2ZXJ0aXNpbmciLCAiaXNfZ2FtZXN2aWRlbyIsICJpc19lY29tbWVyY2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAiaXNfYmlvdGVjaCIsICJpc19jb25zdWx0aW5nIiwgImlzX290aGVyY2F0ZWdvcnkiLCAKICAgICAgICAgICAgICAgICAgICAgICAiaGFzX1ZDIiwgImhhc19hbmdlbCIsICJoYXNfcm91bmRBIiwgImhhc19yb3VuZEIiLCAKICAgICAgICAgICAgICAgICAgICAgICAiaGFzX3JvdW5kQyIsICJoYXNfcm91bmREIiwgImlzX3RvcDUwMCIsICJzdGF0dXMiKQoKIyBNZWx0IHRoZSBkYXRhc2V0cyBmb3IgZWFzaWVyIHBsb3R0aW5nCm1lbHRlZF9kYXRhIDwtIGdhdGhlcihkYXRhc2V0LCBrZXkgPSAidmFyaWFibGUiLCB2YWx1ZSA9ICJ2YWx1ZSIsIGFsbF9vZihiaW5hcnlfYXR0cmlidXRlcykpCgojIENyZWF0ZSBiYXIgcGxvdHMgd2l0aCBmYWNldCB3cmFwCmJhcnBsb3RfYmluYXJ5X2FmdGVyIDwtIGdncGxvdChtZWx0ZWRfZGF0YSwgYWVzKHggPSB2YWx1ZSwgZmlsbCA9IHZhcmlhYmxlKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJjb3VudCIsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrZ3JheSIpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArCiAgbGFicyh0aXRsZSA9ICJCYXIgUGxvdHMgZm9yIEJpbmFyeSBBdHRyaWJ1dGVzIEFGVEVSIFByZS1wcm9jZXNzaW5nIiwgeCA9ICJWYWx1ZSIsIHkgPSAiQ291bnQiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgojIERpc3BsYXkgdGhlIHBsb3QKcHJpbnQoYmFycGxvdF9iaW5hcnlfYmVmb3JlKQpwcmludChiYXJwbG90X2JpbmFyeV9hZnRlcikKYGBgCiBUaGUgYmluYXJ5IGF0dHJpYnV0ZXMgaGF2ZSByZW1haW5lZCB1bmFsdGVyZWQ7IG5vIGNoYW5nZXMgaGF2ZSBiZWVuIGFwcGxpZWQuPGJyPjxicj4KCiMjIFBsb3R0aW5nIG9mIFByZS1wcm9jZXNzZWQgRGF0YSAKClBsb3R0aW5nIHByZS1wcm9jZXNzZWQgZGF0YSBpcyBjcnVjaWFsIGFzIGl0IHZpc3VhbGx5IHVudmVpbHMgcGF0dGVybnMsIHRyZW5kcywgYW5kIGRpc3RyaWJ1dGlvbnMgd2l0aGluIHRoZSBkYXRhc2V0LiBJdCBoZWxwcyB1bmRlcnN0YW5kIGF0dHJpYnV0ZSBkaXN0cmlidXRpb25zLCBjb3JyZWxhdGlvbnMsIGFuZCBzcG90IHZhcmlhdGlvbnMgcG9zdC1wcmVwcm9jZXNzaW5nLCB3aGljaCBpcyBmdW5kYW1lbnRhbCBmb3IgbWFraW5nIGluZm9ybWVkIGRlY2lzaW9ucyBhbmQgdW5jb3ZlcmluZyBpbnNpZ2h0cyBpbiB0aGUgZGF0YSBhbmFseXNpcyBwcm9jZXNzLjxicj4KCiMjIyBBKSBSZWxhdGlvbnNoaXAgYmV0d2VlbiBUb3AgNTAwIENvbXBhbmllcyBhbmQgU3RhdHVzCgpgYGB7cn0KY29yKGRhdGFzZXQkaXNfdG9wNTAwLCBkYXRhc2V0JHN0YXR1cykKYGBgCkEgY29ycmVsYXRpb24gY29lZmZpY2llbnQgb2YgMC4zMTUgc3VnZ2VzdHMgYSBtb2RlcmF0ZSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBhdHRyaWJ1dGVzIOKAmGlzX3RvcDUwMOKAmSBhbmQgdGhlIGNsYXNzIGxhYmVsIOKAmHN0YXR1c+KAmS4gVGhpcyBpbXBsaWVzIHRoYXQgY2hhbmdlcyBpbiBvbmUgdmFyaWFibGUgYXJlIGFzc29jaWF0ZWQgd2l0aCByZWxhdGl2ZWx5IHByb3BvcnRpb25hbCBjaGFuZ2VzIGluIHRoZSBvdGhlciB2YXJpYWJsZSwgYWxiZWl0IG5vdCBwZXJmZWN0bHkuCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKIyBDcmVhdGUgYSBzdW1tYXJ5IHRhYmxlIHRvIGNvdW50IHRoZSBjb21iaW5hdGlvbnMgb2YgaXNfdG9wNTAwIGFuZCBzdGF0dXMKc3VtbWFyeV90YWJsZSA8LSB0YWJsZShkYXRhc2V0JGlzX3RvcDUwMCwgZGF0YXNldCRzdGF0dXMpCgojIENvbnZlcnQgdGhlIHN1bW1hcnkgdGFibGUgdG8gYSBkYXRhIGZyYW1lCnN1bW1hcnlfZGYgPC0gYXMuZGF0YS5mcmFtZShzdW1tYXJ5X3RhYmxlKQoKIyBSZW5hbWUgdGhlIGNvbHVtbnMgZm9yIGNsYXJpdHkKY29sbmFtZXMoc3VtbWFyeV9kZikgPC0gYygiaXNfdG9wNTAwIiwgInN0YXR1cyIsICJjb3VudCIpCgojIENyZWF0ZSBhIGJhcnBsb3QKZ2dwbG90KHN1bW1hcnlfZGYsIGFlcyh4ID0gaXNfdG9wNTAwLCB5ID0gY291bnQsIGZpbGwgPSBzdGF0dXMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIiwgYWVzKGZpbGwgPSBzdGF0dXMpLCBjb2xvciA9ICJibGFjayIpICsKICBsYWJzKHRpdGxlID0gIlRvcCA1MDAgQ29tcGFueSB2cy4gU3RhdHVzIiwgeCA9ICJUb3AgNTAwIiwgeSA9ICJDb3VudCIpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJhY3F1aXJlZCIgPSAiZGFya2dyYXkiLCAiY2xvc2VkIiA9ICJkYXJrZ3JheSIpKQpgYGAKKipJcyBiZWluZyBhIFRvcCA1MDAgY29tcGFueSBhIHN0cm9uZyBpbmRpY2F0b3Igb2Ygd2hldGhlciBhIHN0YXJ0dXAgd2lsbCBiZSBhY3F1aXJlZCBvciBjbG9zZWQ/KiogVGhlIHZhc3QgbWFqb3JpdHkgb2YgdG9wIDUwMCBjb21wYW5pZXMgYXJlIGFjcXVpcmVkIGNvbXBhbmllcy48YnI+CgoKIyMjIEIpIERpc3RyaWJ1dGlvbiBvZiBTdGFydHVwIFNlY3RvcnMKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2NhbGVzKQoKIyBEZWZpbmUgc2hhZGVzIG9mIGdyYXkKY29sb3JzIDwtIGMoIiNGRkZGRkYiLCAiI0Y5RjlGOSIsICIjRjJGMkYyIiwgIiNFNUU1RTUiLCAiI0Q5RDlEOSIsICIjQ0NDQ0NDIiwgIiNCMkIyQjIiLCAiIzk5OTk5OSIsICIjODA4MDgwIiwgIiM2NjY2NjYiKSAKCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmb3IgeW91ciBiaW5hcnkgYXR0cmlidXRlcwpiaW5hcnlfZGF0YSA8LSBkYXRhLmZyYW1lKAogIEF0dHJpYnV0ZSA9IGMoImlzX3NvZnR3YXJlIiwgImlzX3dlYiIsICJpc19tb2JpbGUiLCAiaXNfZW50ZXJwcmlzZSIsICJpc19hZHZlcnRpc2luZyIsICJpc19nYW1lc3ZpZGVvIiwgImlzX2Vjb21tZXJjZSIsICJpc19iaW90ZWNoIiwgImlzX2NvbnN1bHRpbmciLCAiaXNfb3RoZXJjYXRlZ29yeSIpLAogIFZhbHVlID0gYyhzdW0oZGF0YXNldCRpc19zb2Z0d2FyZSksIHN1bShkYXRhc2V0JGlzX3dlYiksIHN1bShkYXRhc2V0JGlzX21vYmlsZSksIHN1bShkYXRhc2V0JGlzX2VudGVycHJpc2UpLCBzdW0oZGF0YXNldCRpc19hZHZlcnRpc2luZyksIHN1bShkYXRhc2V0JGlzX2dhbWVzdmlkZW8pLCBzdW0oZGF0YXNldCRpc19lY29tbWVyY2UpLCBzdW0oZGF0YXNldCRpc19iaW90ZWNoKSwgc3VtKGRhdGFzZXQkaXNfY29uc3VsdGluZyksIHN1bShkYXRhc2V0JGlzX290aGVyY2F0ZWdvcnkpKQopCgojIENhbGN1bGF0ZSBwZXJjZW50YWdlcwpiaW5hcnlfZGF0YSRQZXJjZW50YWdlIDwtIChiaW5hcnlfZGF0YSRWYWx1ZSAvIHN1bShiaW5hcnlfZGF0YSRWYWx1ZSkpICogMTAwCgojIENyZWF0ZSB0aGUgcGllIGNoYXJ0IHdpdGggc2hhZGVzIG9mIGdyYXkKcGllX2NoYXJ0IDwtIGdncGxvdChiaW5hcnlfZGF0YSwgYWVzKHggPSAiIiwgeSA9IFBlcmNlbnRhZ2UsIGZpbGwgPSBBdHRyaWJ1dGUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSwgY29sb3I9ImJsYWNrIikgKwogIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKSArCiAgbGFicyh0aXRsZSA9ICJTdGFydHVwIENhdGVnb3J5IikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9ycykgKyAgIyBTZXQgdGhlIGNvbG9ycwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50X2Zvcm1hdChzY2FsZSA9IDEpKQoKIyBEaXNwbGF5IHRoZSBwaWUgY2hhcnQKcHJpbnQocGllX2NoYXJ0KQpgYGAKCgoqKldoYXQgYXJlIHRoZSBtb3N0IHBvcHVsYXIgc3RhcnR1cCBzZWN0b3JzPyoqIFRoZSBhbnN3ZXIgaXMgc29mdHdhcmUuIE1vc3Qgc3RhcnR1cHMgZm9jdXMgb24gdGVjaC1yZWxhdGVkIHNlY3RvcnMsIGxpa2Ugc29mdHdhcmUsIHdlYiwgYW5kIG1vYmlsZS48YnI+CgoKIyMjIEMpIERpc3RyaWJ1dGlvbiBvZiBTdGFydHVwIFN0YXRlIG9mIE9yaWdpbgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIHRoZSBjb3VudHMgb2YgZWFjaCBiaW5hcnkgYXR0cmlidXRlCmJpbmFyeV9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgQXR0cmlidXRlID0gYygiaXNfQ0EiLCAiaXNfTlkiLCAiaXNfTUEiLCAiaXNfVFgiLCAiaXNfb3RoZXJzdGF0ZSIpLAogIENvdW50ID0gYygKICAgIHN1bShkYXRhc2V0JGlzX0NBKSwKICAgIHN1bShkYXRhc2V0JGlzX05ZKSwKICAgIHN1bShkYXRhc2V0JGlzX01BKSwKICAgIHN1bShkYXRhc2V0JGlzX1RYKSwKICAgIHN1bShkYXRhc2V0JGlzX290aGVyc3RhdGUpCiAgKQopCgojIENhbGN1bGF0ZSBwZXJjZW50YWdlcwpiaW5hcnlfZGF0YSRQZXJjZW50YWdlIDwtIChiaW5hcnlfZGF0YSRDb3VudCAvIHN1bShiaW5hcnlfZGF0YSRDb3VudCkpICogMTAwCgojIENyZWF0ZSBhIHBpZSBjaGFydApwaWVfY2hhcnQgPC0gZ2dwbG90KGJpbmFyeV9kYXRhLCBhZXMoeCA9ICIiLCB5ID0gUGVyY2VudGFnZSwgZmlsbCA9IEF0dHJpYnV0ZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxLCBjb2xvcj0iYmxhY2siKSArCiAgY29vcmRfcG9sYXIodGhldGEgPSAieSIpICsgICMgQ29udmVydCB0byBwb2xhciBjb29yZGluYXRlcyBmb3IgYSBwaWUgY2hhcnQKICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBTdGFydHVwIFN0YXRlIG9mIE9yaWdpbiIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJpc19DQSIgPSAibGlnaHRncmF5IiwgImlzX05ZIiA9ICJkYXJrZ3JheSIsICJpc19NQSIgPSAiZGFya2dyYXkiLCAiaXNfVFgiID0gImRhcmtncmF5IiwgImlzX290aGVyc3RhdGUiID0gImRhcmtncmF5IikpICsKICB0aGVtZV9taW5pbWFsKCkgKyAgCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChyb3VuZChQZXJjZW50YWdlLCAxKSwgIiUiKSksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKQoKIyBEaXNwbGF5IHRoZSBwaWUgY2hhcnQKcHJpbnQocGllX2NoYXJ0KQpgYGAKKipXaGljaCBzdGF0ZSBpcyB0aGUgbW9zdCBwb3B1bGFyIGNob2ljZSBmb3Igc3RhcnR1cHMgdG8gbGF1bmNoIGluPyoqIFRoZSBhbnN3ZXIgaXMgQ2FsaWZvcm5pYS4gNTEuNCUgb2YgYWxsIHN0YXJ0dXBzIGxhdW5jaGVkIGZyb20gQ2FsaWZvcm5pYS48YnI+CgojIyMgRCkgRGlzdHJpYnV0aW9uIG9mIEZ1bmRpbmcgUm91bmRzIGFuZCBTdGF0dXMKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeSh0aWR5cikgICMgTG9hZCB0aGUgdGlkeXIgcGFja2FnZQoKIyBGaWx0ZXIgcm93cyB3aGVyZSBzdGF0dXMgaXMgImFjcXVpcmVkIgphY3F1aXJlZF9kYXRhIDwtIHN1YnNldChkYXRhc2V0LCBzdGF0dXMgPT0gIjEiKQoKIyBDcmVhdGUgYSBsb25nLWZvcm1hdCBkYXRhc2V0IGZvciB1c2Ugd2l0aCBnZ3Bsb3QyCmFjcXVpcmVkX2RhdGFfbG9uZyA8LSB0aWR5cjo6Z2F0aGVyKGFjcXVpcmVkX2RhdGEsIGtleSA9ICJBdHRyaWJ1dGUiLCB2YWx1ZSA9ICJCaW5hcnlWYWx1ZSIsIGhhc19WQywgaGFzX2FuZ2VsLCBoYXNfcm91bmRBLCBoYXNfcm91bmRCLCBoYXNfcm91bmRDLCBoYXNfcm91bmREKQoKIyBDcmVhdGUgaGlzdG9ncmFtcyB3aXRoIGZhY2V0X3dyYXAgZm9yICJhY3F1aXJlZCIgc3RhdHVzCnBsb3QxIDwtIGdncGxvdChhY3F1aXJlZF9kYXRhX2xvbmcsIGFlcyh4ID0gQmluYXJ5VmFsdWUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBmaWxsID0gImRhcmtncmF5IiwgY29sb3IgPSAiYmxhY2siKSArCiAgZmFjZXRfd3JhcCh+IEF0dHJpYnV0ZSwgc2NhbGVzID0gImZyZWVfeCIpICsKICBsYWJzKHRpdGxlID0gIidBY3F1aXJlZCcgU3RhdHVzIiwgeCA9ICJWYWx1ZSIsIHkgPSAiRnJlcXVlbmN5IikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKIyBGaWx0ZXIgcm93cyB3aGVyZSBzdGF0dXMgaXMgImNsb3NlZCIKY2xvc2VkX2RhdGEgPC0gc3Vic2V0KGRhdGFzZXQsIHN0YXR1cyA9PSAiMCIpCgojIENyZWF0ZSBhIGxvbmctZm9ybWF0IGRhdGFzZXQgZm9yIHVzZSB3aXRoIGdncGxvdDIKY2xvc2VkX2RhdGFfbG9uZyA8LSB0aWR5cjo6Z2F0aGVyKGNsb3NlZF9kYXRhLCBrZXkgPSAiQXR0cmlidXRlIiwgdmFsdWUgPSAiQmluYXJ5VmFsdWUiLCBoYXNfVkMsIGhhc19hbmdlbCwgaGFzX3JvdW5kQSwgaGFzX3JvdW5kQiwgaGFzX3JvdW5kQywgaGFzX3JvdW5kRCkKCiMgQ3JlYXRlIGhpc3RvZ3JhbXMgd2l0aCBmYWNldF93cmFwIGZvciAiY2xvc2VkIiBzdGF0dXMKcGxvdDIgPC0gZ2dwbG90KGNsb3NlZF9kYXRhX2xvbmcsIGFlcyh4ID0gQmluYXJ5VmFsdWUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBmaWxsID0gImRhcmtncmF5IiwgY29sb3IgPSAiYmxhY2siKSArCiAgZmFjZXRfd3JhcCh+IEF0dHJpYnV0ZSwgc2NhbGVzID0gImZyZWVfeCIpICsKICBsYWJzKHRpdGxlID0gIidDbG9zZWQnIFN0YXR1cyIsIHggPSAiVmFsdWUiLCB5ID0gIkZyZXF1ZW5jeSIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCiMgUGxvdHRpbmcgdGhlIHR3byBoaXN0b2dyYW1zIHNpZGUgYnkgc2lkZQpwbG90X2dyaWQocGxvdDEsIHBsb3QyLCBucm93ID0gMSkKYGBgCioqRG8gaW5jcmVhc2VkIGZ1bmRpbmcgcm91bmRzIHNlcnZlIGFzIGFuIGluZGljYXRvciBvZiB3aGV0aGVyIGEgc3RhcnR1cCBpcyBtb3JlIGxpa2VseSB0byBiZSBhY3F1aXJlZCBvciBjbG9zZWQgaW4gdGhlIGZ1dHVyZT8qKiBUaGUgYW5zd2VyIGlzIG5vdCBuZWNlc3NhcmlseSwgYnV0IGZyb20gdGhlIGdyYXBocyB3ZSBjYW4gc2VlIHRoYXQgdGhlIG51bWJlciBvZiBhY3F1aXJlZCBzdGFydHVwcyB0aGF0IHdlbnQgdGhyb3VnaCBzZXJpZXMgQSwgQiwgQywgYW5kIEQgaXMgbW9yZSB0aGFuIHRoZSBudW1iZXIgb2YgY2xvc2VkIHN0YXJ0dXBzIHRoYXQgd2VudCB0aHJvdWdoIHRoZW0uIFRoZSBtYWpvcml0eSBvZiAiYWNxdWlyZWQiIHN0YXJ0dXBzIGhhZCBhIHNlcmllcyBBIGZ1bmRpbmcgcm91bmQsIHdoaWxlIHRoZSBtYWpvcml0eSBvZiAiY2xvc2VkIiBzdGFydHVwcyBkaWQgbm90LiBTZXJpZXMgQSBhbmQgU2VyaWVzIEIgZnVuZGluZyBjb3VsZCBiZSBhIHBvdGVudGlhbCBpbmRpY2F0b3Igb2Ygc3RhcnR1cCBzdWNjZXNzLiBBbmdlbCBpbnZlc3RvcnMgaW52ZXN0ZWQgYWxtb3N0IGVxdWFsbHkgaW4gYm90aCAiYWNxdWlyZWQiIGFuZCAiY2xvc2VkIiBzdGFydHVwcy4gVkNzIHRlbmQgdG8gaW52ZXN0IG1vcmUgaW4gc3RhcnR1cHMgdGhhdCBiZWNvbWUgImFjcXVpcmVkIiBpbiB0aGUgZnV0dXJlLiBOZXZlcnRoZWxlc3MsIHRoZSByZXN1bHRzIG1heSBiZSBpbmFjY3VyYXRlIGJlY2F1c2UgdGhlIGNsYXNzIGxhYmVsIGlzIGltYmFsYW5jZWQuPGJyPgoKCiMjIyBFKSBSZWxhdGlvbnNoaXAgQmV0d2VlbiB0aGUgTnVtYmVyIG9mIEZ1bmRpbmcgUm91bmRzIGFuZCBUb3RhbCBGdW5kaW5nIFJhaXNlZAoKYGBge3J9CnJhbmdlKGRhdGFzZXQkZnVuZGluZ190b3RhbF91c2QpCmBgYApBdHRyaWJ1dGUgZnVuZGluZ190b3RhbF91c2Qgd2FzIG5vcm1hbGl6ZWQgdXNpbmcgbWluLW1heCBub3JtYWxpemF0aW9uLiBTbywgdGhlIHJhbmdlIG9mIGZ1bmRpbmdfdG90YWxfdXNkIHZhbHVlcyBmYWxsIHdpdGhpbiAwIChtaW5pbXVtKSBhbmQgMSAobWF4aW11bSkuIAoKYGBge3J9CnJhbmdlKGRhdGFzZXQkZnVuZGluZ19yb3VuZHMpCmBgYApUaGUgbGVhc3QgYW1vdW50IG9mIGZ1bmRpbmcgcm91bmRzIHdlbnQgdGhyb3VnaCBieSBhbnkgc3RhcnR1cCBpcyAxLCBpbmRpY2F0aW5nIHRoYXQgYWxsIHN0YXJ0dXBzIGF0IGxlYXN0IHdlbnQgdGhyb3VnaCBvbmUgZnVuZGluZyByb3VuZC4gVGhlIG1vc3QgYW1vdW50IG9mIGZ1bmRpbmcgcm91bmRzIHdlbnQgdGhyb3VnaCBieSBhbnkgc3RhcnR1cCBpcyA2LiAKCmBgYHtyfQpjb3IoZGF0YXNldCRmdW5kaW5nX3JvdW5kcywgZGF0YXNldCRmdW5kaW5nX3RvdGFsX3VzZCkKYGBgCkEgY29ycmVsYXRpb24gb2YgMC40MzkgaW5kaWNhdGVzIGEgbW9kZXJhdGUgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdmFyaWFibGVzICdmdW5kaW5nX3JvdW5kcycgYW5kICdmdW5kaW5nX3RvdGFsX3VzZCcuCgpgYGB7cn0KYm94cGxvdChkYXRhc2V0JGZ1bmRpbmdfdG90YWxfdXNkIH4gZGF0YXNldCRmdW5kaW5nX3JvdW5kcywKICAgICAgICB4bGFiID0gIk51bWJlciBvZiBGdW5kaW5nIFJvdW5kcyIsCiAgICAgICAgeWxhYiA9ICJGdW5kaW5nIFRvdGFsIChVU0QpIiwKICAgICAgICBtYWluID0gIkZ1bmRpbmcgUm91bmRzIHZzIEZ1bmRpbmcgVG90YWwiLAogICAgICAgIGNvbCA9ICJkYXJrZ3JheSIpCmBgYAoqKklzIHRoZXJlIGEgc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIG51bWJlciBvZiBmdW5kaW5nIHJvdW5kcyBhbmQgdGhlIHRvdGFsIGZ1bmRpbmcgcmVjZWl2ZWQgYnkgYSBjb21wYW55PyoqIEl04oCZcyBnZW5lcmFsbHkgdHJ1ZSwgYnV0IG5vdCBhbiBhYnNvbHV0ZSBydWxlLiBUaGVyZeKAmXMgb2Z0ZW4gYSBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBpbmNyZWFzZWQgZnVuZGluZyByb3VuZHMgYW5kIHJhaXNlZCBmdW5kcywgaW5kaWNhdGluZyB0aGF0IG1vcmUgcm91bmRzIHRlbmQgdG8geWllbGQgbW9yZSBtb25leS48YnI+CgoKIyMjIEYpIEFzc29jaWF0aW9uIEJldHdlZW4gVG90YWwgRnVuZGluZyBhbmQgU3RhcnR1cCBDYXRlZ29yeQoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKCiMgU3VtbWFyeSB0YWJsZTogU3VtIG9mIGZ1bmRpbmcgZm9yIGVhY2ggY2F0ZWdvcnkKY2F0ZWdvcnlfZnVuZGluZ19zdW1tYXJ5IDwtIGRhdGFzZXQgJT4lCiAgZ3JvdXBfYnkoaXNfc29mdHdhcmUsIGlzX3dlYiwgaXNfbW9iaWxlLCBpc19lbnRlcnByaXNlLAogICAgICAgICAgIGlzX2FkdmVydGlzaW5nLCBpc19nYW1lc3ZpZGVvLCBpc19lY29tbWVyY2UsIGlzX2Jpb3RlY2gsIGlzX2NvbnN1bHRpbmcsIGlzX290aGVyY2F0ZWdvcnkpICU+JQogIHN1bW1hcmlzZSh0b3RhbF9mdW5kaW5nID0gc3VtKGZ1bmRpbmdfdG90YWxfdXNkKSkKCiMgUmVzaGFwZSB0aGUgZGF0YSBmb3IgcGxvdHRpbmcKY2F0ZWdvcnlfZnVuZGluZ19zdW1tYXJ5IDwtIGNhdGVnb3J5X2Z1bmRpbmdfc3VtbWFyeSAlPiUKICBwaXZvdF9sb25nZXIoCiAgICBjb2xzID0gLXRvdGFsX2Z1bmRpbmcsCiAgICBuYW1lc190byA9ICJDYXRlZ29yeSIsCiAgICB2YWx1ZXNfdG8gPSAiQmluYXJ5VmFsdWUiCiAgKQoKIyBDcmVhdGUgYSBiYXIgcGxvdCB3aXRoIGdyYXkgY29sb3JzCmdncGxvdChjYXRlZ29yeV9mdW5kaW5nX3N1bW1hcnksIGFlcyh4ID0gQ2F0ZWdvcnksIHkgPSB0b3RhbF9mdW5kaW5nLCBmaWxsID0gQmluYXJ5VmFsdWUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIEZ1bmRpbmcgdnMgU3RhcnR1cCBDYXRlZ29yeSIsIHggPSAiQ2F0ZWdvcnkiLCB5ID0gIlRvdGFsIEZ1bmRpbmciKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgCmBgYAoqKklzIHRoZXJlIGEgc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHNlY3RvciBvZiB0aGUgY29tcGFueSBhbmQgdGhlIHRvdGFsIGZ1bmRpbmcgcmVjZWl2ZWQgYnkgYSBjb21wYW55PyoqIFRoZSBhbnN3ZXIgaXMgeWVzLiBTb2Z0d2FyZSBzdGFydHVwcyByZWNlaXZlZCB0aGUgaGlnaGVzdCBhbW91bnRzIG9mIGZ1bmRpbmcuIEZvbGxvd2VkIGJ5IG90aGVyIHRlY2gtcmVsYXRlZCBzZWN0b3JzOiB3ZWIgYW5kIG1vYmlsZS48YnI+PGJyPjxicj4KCgojIyA1LURhdGEgTWluaW5nIFRlY2huaXF1ZXMKClRvIGlkZW50aWZ5IHBhdHRlcm5zIGFuZCBtYWtlIHByZWRpY3Rpb25zIGJhc2VkIG9uIHRoZSBkYXRhIG9mIHRoZSBkYXRhc2V0LCB3ZSBhcmUgZ29pbmcgdG8gcGVyZm9ybSB0aGUgZGF0YSBtaW5pbmcgdGVjaG5pcXVlcyBjbGFzc2lmaWNhdGlvbiBhbmQgY2x1c3RlcmluZy4gKipDbGFzc2lmaWNhdGlvbioqIGlzIGEgc3VwZXJ2aXNlZCBsZWFybmluZyB0ZWNobmlxdWUgdGhhdCB1c2VzIGxhYmVsZWQgZGF0YSB0byB0cmFpbiBhIG1vZGVsIHRvIHByZWRpY3QgdGhlIGNsYXNzIG9mIG5ldyBkYXRhIHBvaW50cy4gKipDbHVzdGVyaW5nKiogaXMgYW4gdW5zdXBlcnZpc2VkIGxlYXJuaW5nIHRlY2huaXF1ZSB0aGF0IGdyb3VwcyB1bmxhYmVsZWQgZGF0YSBwb2ludHMgaW50byBjbHVzdGVycyBiYXNlZCBvbiB0aGVpciBzaW1pbGFyaXRpZXMuIDxicj48YnI+CgoKIyMgNS4xLUNsYXNzaWZpY2F0aW9uIAoKQXMgbWVudGlvbmVkIGJlZm9yZSwgKipjbGFzc2lmaWNhdGlvbiBpcyBhIGZvcm0gb2Ygc3VwZXJ2aXNlZCBsZWFybmluZywgd2hlcmUgdGhlIGNsYXNzIGxhYmVsIGlzIGtub3duIGJlZm9yZWhhbmQqKi4gSW4gdGhlIGNhc2Ugb2Ygc3RhcnR1cCBkYXRhLCB0aGUgY2xhc3MgbGFiZWwgaXMgdGhlIGJpbmFyeSBhdHRyaWJ1dGUgInN0YXR1cyIuIFN0YXR1cyBhdHRyaWJ1dGUgaG9sZHMgdHdvIHZhbHVlcywgZWl0aGVyIDEgZm9yICJhY3F1aXJlZCIgc3RhdHVzLCBvciAwIGZvciAiY2xvc2VkIiBzdGF0dXMuIEluIGNsYXNzaWZpY2F0aW9uLCB0aGUgYWxnb3JpdGhtIGxlYXJucyBmcm9tIGxhYmVsZWQgdHJhaW5pbmcgZGF0YSwgdGhlbiBlc3RhYmxpc2hlcyBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGlucHV0IGZlYXR1cmVzIGFuZCB0aGVpciByZXNwZWN0aXZlIGNsYXNzZXMuIFRoZSBsZWFybmVkIHBhdHRlcm5zIGFyZSB0aGVuIHVzZWQgdG8gY2xhc3NpZnkgbmV3LCB1bnNlZW4gZGF0YS4gCgpJbiB0aGlzIHByb2plY3QsIHdlIGFyZSBnb2luZyB0byB1c2UgdGhlICoqZGVjaXNpb24gdHJlZSoqIGFsZ29yaXRobSB0byBwZXJmb3JtIGNsYXNzaWZpY2F0aW9uLiBEZWNpc2lvbiB0cmVlIGlzIGNvbnNpZGVyZWQgYSBncmVlZHkgYWxnb3JpdGhtLiBUaGUgdHJlZSBpcyBjb25zdHJ1Y3RlZCBpbiBhIHRvcC1kb3duIHJlY3Vyc2l2ZSBkaXZpZGUtYW5kLWNvbnF1ZXIgbWFubmVyLiBJdCB0YWtlcyB0aGUgZm9ybSBvZiBhIGJyYW5jaGluZyB0cmVlOyB0aGUgdG9wIG5vZGUgaXMgdGhlIGRlY2lzaW9uIG5vZGUgYW5kIHRoZSBib3R0b20gdW5icmFuY2hlZCBsZWF2ZXMgcmVwcmVzZW50IHRoZSBjbGFzcy4gCgpUaGUgZm9sbG93aW5nIGNsYXNzaWZpY2F0aW9uIHN0ZXBzIHdpbGwgYmUgcGVyZm9ybWVkOgoKMS4gQ29uc3RydWN0IGEgbW9kZWwgdXNpbmcgdHJhaW5pbmcgZGF0YS4gCjIuIEV2YWx1YXRlIHRoZSBtb2RlbCB1c2luZyB0ZXN0aW5nIGRhdGEuIAozLiBQcmVkaWN0IHRoZSBjbGFzcyB1c2luZyBuZXcgZGF0YS4KCiMjIyBQYXJ0aXRpb25pbmcgTWV0aG9kCgoqKldlIHdpbGwgdXNlIHRoZSBob2xkLW91dCBtZXRob2QgYXMgdGhlIHBhcnRpdGlvbmluZyBtZXRob2QuIFRoZSBkYXRhc2V0IHdpbGwgYmUgc2VjdGlvbmVkIGludG8gdGhyZWUgcGFydGl0aW9uczogNzA6MzAgZGF0YSBzcGxpdCwgODA6MjAgZGF0YSBzcGxpdCwgYW5kIDkwOjEwIGRhdGEgc3BsaXRzLioqIFRoZSBob3VsZC1vdXQgc3BsaXQgbWV0aG9kIGlzIGEgY29tbW9uIHByYWN0aWNlIGluIG1hY2hpbmUgbGVhcm5pbmcuIEl0IGhlbHBzIGluIG1heGltaXppbmcgdGhlIHVzYWdlIG9mIGF2YWlsYWJsZSBkYXRhIGZvciBib3RoIHRyYWluaW5nIGFuZCB0ZXN0aW5nLiBCeSBwZXJmb3JtaW5nIHRoaXMgIG1ldGhvZCwgd2UgY2FuIHByZXZlbnQgYW4gb3ZlcmZpdHRlZCBtb2RlbCBieSBwcm92aWRpbmcgYSBzZXBhcmF0ZSBkYXRhIHNldCBmb3IgbW9kZWwgZXZhbHVhdGlvbi5Gb3IgZWFjaCBzcGxpdCB3ZSB3aWxsIHJ1biBJbmZvcm1hdGlvbiBHYWluLCBHYWluIFJhdGlvbiwgYW5kIEdpbmkgSW5kZXguIFVsdGltYXRlbHksIHdlIHdpbGwgaGF2ZSBhIHRvdGFsIG9mIG5pbmUgZGVjaXNpb24gdHJlZXMuCgoqKuKXhiBJbmZvcm1hdGlvbiBHYWluKiogaXMgaXMgYSBtZWFzdXJlIHVzZWQgaW4gdGhlIGRlY2lzaW9uIHRyZWUgYWxnb3JpdGhtLiBJdCByZXByZXNlbnRzIHRoZSBhbW91bnQgb2YgZGlzb3JkZXIgb3IgdW5jZXJ0YWludHkgaW4gYSBkYXRhc2V0IHRoYXQgaXMgcmVkdWNlZCAob3IgZ2FpbmVkKSBhZnRlciBzcGxpdHRpbmcgdGhlIGRhdGEgb24gYW4gYXR0cmlidXRlLiBJdCBtZWFzdXJlcyB0aGUgcmVkdWN0aW9uIGluIGVudHJvcHkgKGltcHVyaXR5IG9yIGRpc29yZGVyKSBhZnRlciB0aGUgZGF0YXNldCBpcyBzcGxpdC4gQXR0cmlidXRlIHdpdGggdGhlICoqbWF4aW11bSBpbmZvcm1hdGlvbiBnYWluIGlzIHNlbGVjdGVkIGFzIHRoZSBkZWNpc2lvbiBub2RlKiouIAoKKiril4YgR2FpbiBSYXRpbyoqIGlzIGNvbnNpZGVycyB0aGUgSW5mb3JtYXRpb24gR2FpbiBidXQgbm9ybWFsaXplcyBpdCBieSB0aGUgaW50cmluc2ljIGluZm9ybWF0aW9uIGFzc29jaWF0ZWQgd2l0aCBlYWNoIHNwbGl0LiBJdCBhY2NvdW50cyBmb3IgdGhlIHNpemUgb2YgdGhlIGJyYW5jaGVzIHJlc3VsdGluZyBmcm9tIGEgc3BsaXQuIEF0dHJpYnV0ZSB3aXRoIHRoZSBtYXhpbXVtIGdhaW4gcmF0aW8gaXMgc2VsZWN0ZWQgYXMgdGhlIHNwbGl0dGluZyBhdHRyaWJ1dGUuIEF0dHJpYnV0ZSB3aXRoIHRoZSAqKm1heGltdW0gZ2FpbiByYXRpbyBpcyBzZWxlY3RlZCBhcyB0aGUgc3BsaXR0aW5nIGF0dHJpYnV0ZSoqLgoKKiril4YgR2luaSBJbmRleCoqIGlzIGlzIGEgbWVhc3VyZSBvZiBpbXB1cml0eSBvciB0aGUgcXVhbGl0eSBvZiBhIHNwbGl0IGluIGEgZGF0YXNldC4gSW4gdGhlIGNvbnRleHQgb2YgZGVjaXNpb24gdHJlZXMsIGl0IGNhbGN1bGF0ZXMgdGhlIG92ZXJhbGwgcHJvYmFiaWxpdHkgb2YgYSBzcGVjaWZpYyBmZWF0dXJlIGJlaW5nIG1pc2NsYXNzaWZpZWQuIEF0dHJpYnV0ZSB3aXRoIHRoZSAqKm1pbmltdW0gZ2luaSBpbmRleCBpcyBzZWxlY3RlZCBhcyB0aGUgc3BsaXR0aW5nIGF0dHJpYnV0ZSoqLgoKQmVmb3JlIHdlIHN0YXJ0IGxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBzdGFydHVwIGRhdGEgYWZ0ZXIgcHJlLXByb2Nlc3Npbmc6CgpgYGB7cn0KbGlicmFyeShyZWFkeGwpCnByZXByb2Nlc3NlZF9kYXRhc2V0IDwtIHJlYWRfZXhjZWwoIlByZXByb2Nlc3NlZF9TdGFydHVwRGF0YS54bHN4IikKc3RyKHByZXByb2Nlc3NlZF9kYXRhc2V0KQpgYGAKCkFsbCBhdHRyaWJ1dGVzIGhhdmUgYmVlbiBjaGFuZ2VkIHRvIGEgbnVtZXJpY2FsIGZvcm1hdCB0byBlbnN1cmUgc21vb3RoIHV0aWxpemF0aW9uIG9mIGNsYXNzaWZpY2F0aW9uIHRyZWUgcGxvdHRpbmcgZnVuY3Rpb25zLgoKCiMjIyBCYWxhbmNpbmcgdGhlIENsYXNzIExhYmVsCgpgYGB7cn0KIyBJZGVudGlmeWluZyB0aGUgcm93cyBvZiBlYWNoIHN0YXR1cwpjbG9zZWRfaW5kaWNlcyA8LSB3aGljaChwcmVwcm9jZXNzZWRfZGF0YXNldCRzdGF0dXMgPT0gMCkKYWNxdWlyZWRfaW5kaWNlcyA8LSB3aGljaChwcmVwcm9jZXNzZWRfZGF0YXNldCRzdGF0dXMgPT0gMSkKCiMgTnVtYmVyIG9mIHNhbXBsZXMgZm9yIGVhY2ggc3RhdHVzIGNsYXNzCm51bV9jbG9zZWQgPC0gbGVuZ3RoKGNsb3NlZF9pbmRpY2VzKQpudW1fYWNxdWlyZWQgPC0gbGVuZ3RoKGFjcXVpcmVkX2luZGljZXMpCgojIFN1YnNhbXBsaW5nIHRvIGJhbGFuY2UgdGhlIGRhdGEKaWYgKG51bV9jbG9zZWQgPiBudW1fYWNxdWlyZWQpIHsKICAjIFN1YnNhbXBsZSB0aGUgImNsb3NlZCIgc3RhdHVzIHRvIG1hdGNoIHRoZSAiYWNxdWlyZWQiIGNvdW50CiAgc2FtcGxlZF9jbG9zZWRfaW5kaWNlcyA8LSBzYW1wbGUoY2xvc2VkX2luZGljZXMsIG51bV9hY3F1aXJlZCkKICBiYWxhbmNlZF9wcmVwcm9jZXNzZWRfZGF0YXNldCA8LSByYmluZChwcmVwcm9jZXNzZWRfZGF0YXNldFtzYW1wbGVkX2Nsb3NlZF9pbmRpY2VzLCBdLCBwcmVwcm9jZXNzZWRfZGF0YXNldFthY3F1aXJlZF9pbmRpY2VzLCBdKQp9IGVsc2UgewogICMgU3Vic2FtcGxlIHRoZSAiYWNxdWlyZWQiIHN0YXR1cyB0byBtYXRjaCB0aGUgImNsb3NlZCIgY291bnQKICBzYW1wbGVkX2FjcXVpcmVkX2luZGljZXMgPC0gc2FtcGxlKGFjcXVpcmVkX2luZGljZXMsIG51bV9jbG9zZWQpCiAgYmFsYW5jZWRfcHJlcHJvY2Vzc2VkX2RhdGFzZXQgPC0gcmJpbmQocHJlcHJvY2Vzc2VkX2RhdGFzZXRbY2xvc2VkX2luZGljZXMsIF0sIHByZXByb2Nlc3NlZF9kYXRhc2V0W3NhbXBsZWRfYWNxdWlyZWRfaW5kaWNlcywgXSkKfQpgYGAKV2UgaGF2ZSBwcmV2aW91c2x5IHNob3duIHRoZSBpbWJhbGFuY2Ugb2YgdGhlIGNsYXNzIGxhYmVsICJzdGF0dXMiIChzZWN0aW9uIDQ6IEFzc2Vzc2luZyBDbGFzcyBMYWJlbCBCYWxhbmNlIEFmdGVyIFByZS1wcm9jZXNzaW5nKS4gVG8gYWRkcmVzcyB0aGUgY2xhc3MgaW1iYWxhbmNlIGlzc3VlLCB3ZSB3aWxsIHVzZSB0aGUgKip1bmRlcnNhbXBsaW5nKiogdGVjaG5pcXVlLiBVbmRlcnNhbXBsaW5nIGlzIGEgYmFsYW5jaW5nIG1ldGhvZCB1c2VkIGluIG1hY2hpbmUgbGVhcm5pbmcgd2hlcmUgeW91IHJlZHVjZSB0aGUgbnVtYmVyIG9mIGluc3RhbmNlcyBvZiB0aGUgb3Zlci1yZXByZXNlbnRlZCBjbGFzcyAoc3RhdHVzID0gImFjcXVpcmVkIiBvciAxKSB0byBtYWtlIGl0IGVxdWFsIHRvIHRoZSBudW1iZXIgb2YgaW5zdGFuY2VzIG9mIHRoZSB1bmRlci1yZXByZXNlbnRlZCBjbGFzcyAoc3RhdHVzID0gImNsb3NlZCIgb3IgMCkuIEJ5IGJhbGFuY2luZyB0aGUgY2xhc3MgZGlzdHJpYnV0aW9uLCB1bmRlcnNhbXBsaW5nIGNhbiBsZWFkIHRvIG1vcmUgcm9idXN0IGFuZCBmYWlyIG1vZGVscywgcHJldmVudGluZyB0aGUgbW9kZWwgZnJvbSBiZWluZyBvdmVybHkgaW5mbHVlbmNlZCBieSB0aGUgbWFqb3JpdHkgY2xhc3MuCgojIyMjIyBWZXJpZnkgQ2xhc3MgTGFiZWwgQmFsYW5jZSAKYGBge3J9CiMgQ291bnQgb2YgZWFjaCBjbGFzcyBpbiB0aGUgb3JpZ2luYWwgcHJlcHJvY2Vzc2VkX2RhdGFzZXQKb3JpZ2luYWxfY2xhc3NfY291bnRzIDwtIHRhYmxlKHByZXByb2Nlc3NlZF9kYXRhc2V0JHN0YXR1cykKCiMgQ291bnQgb2YgZWFjaCBjbGFzcyBpbiB0aGUgYmFsYW5jZWQgcHJlcHJvY2Vzc2VkX2RhdGFzZXQKYmFsYW5jZWRfY2xhc3NfY291bnRzIDwtIHRhYmxlKGJhbGFuY2VkX3ByZXByb2Nlc3NlZF9kYXRhc2V0JHN0YXR1cykKCiMgRGlzcGxheSB0aGUgY291bnRzCnByaW50KCJPcmlnaW5hbCBwcmVwcm9jZXNzZWRfZGF0YXNldCBDbGFzcyBDb3VudHM6IikKcHJpbnQob3JpZ2luYWxfY2xhc3NfY291bnRzKQpwcmludCgiQmFsYW5jZWQgcHJlcHJvY2Vzc2VkX2RhdGFzZXQgQ2xhc3MgQ291bnRzOiIpCnByaW50KGJhbGFuY2VkX2NsYXNzX2NvdW50cykKYGBgClRoZXJlIGFyZSA0NTIgcm93cyBmb3IgYWNxdWlyZWQgc3RhdHVzIChtYWpvcml0eSksIGFuZCAyNzUgcm93cyBmb3IgY2xvc2VkIHN0YXR1cyAobWlub3JpdHkpIGJlZm9yZSBiYWxhbmNpbmcuIApBZnRlciBiYWxhbmNpbmcsIHRoZXJlIGFyZSAyNzUgcm93cyBmb3IgYWNxdWlyZWQgc3RhdHVzIGFuZCAyNzUgcm93cyBmb3IgY2xvc2VkIHN0YXR1cyBzaG93Y2FzaW5nIGEgYmFsYW5jZWQgY2xhc3MgbGFiZWwgcmVhZHkgZm9yIGNsYXNzaWZpY2F0aW9uLiAKCgojIyMgTW9kZWwgRXZhbHVhdGlvbiBNZXRyaWNzCmBgYHtyfQojIEZ1bmN0aW9uIGZvciBNb2RlbCBFdmFsdWF0aW9uCmV2YWx1YXRlX21vZGVsIDwtIGZ1bmN0aW9uKHByZWRpY3Rpb25zLCBhY3R1YWxfbGFiZWxzKSB7CiAgIyBDb25mdXNpb24gTWF0cml4CiAgY29uZnVzaW9uX21hdHJpeCA8LSB0YWJsZShhY3R1YWxfbGFiZWxzLCBwcmVkaWN0aW9ucykKICBwcmludChjb25mdXNpb25fbWF0cml4KQogIAogIFRQIDwtIGNvbmZ1c2lvbl9tYXRyaXhbMSwgMV0KICBUTiA8LSBjb25mdXNpb25fbWF0cml4WzIsIDJdCiAgRlAgPC0gY29uZnVzaW9uX21hdHJpeFsyLCAxXQogIEZOIDwtIGNvbmZ1c2lvbl9tYXRyaXhbMSwgMl0KICAKICAjIENhbGN1bGF0ZSBtZXRyaWNzCiAgYWNjdXJhY3kgPC0gKChUUCArIFROKSAvIHN1bShjb25mdXNpb25fbWF0cml4KSkgKiAxMDAKICBwcmVjaXNpb24gPC0gKFRQIC8gKFRQICsgRlApKSAqIDEwMAogIHNlbnNpdGl2aXR5IDwtIChUUCAvIChUUCArIEZOKSkgKiAxMDAKICBzcGVjaWZpY2l0eSA8LSAoVE4gLyAoVE4gKyBGUCkpICogMTAwCiAgCiAgIyBQcmludCBtZXRyaWNzCiAgY2F0KCJBY2N1cmFjeToiLCBhY2N1cmFjeSwgIiVcbiIpCiAgY2F0KCJQcmVjaXNpb246IiwgcHJlY2lzaW9uLCAiJVxuIikKICBjYXQoIlNlbnNpdGl2aXR5IChSZWNhbGwpOiIsIHNlbnNpdGl2aXR5LCAiJVxuIikKICBjYXQoIlNwZWNpZmljaXR5OiIsIHNwZWNpZmljaXR5LCAiJVxuIikKICAKICAjIFJldHVybiBhIGxpc3Qgb2YgbWV0cmljcwogIHJldHVybihsaXN0KAogICAgYWNjdXJhY3kgPSBhY2N1cmFjeSwKICAgIHByZWNpc2lvbiA9IHByZWNpc2lvbiwKICAgIHNlbnNpdGl2aXR5ID0gc2Vuc2l0aXZpdHksCiAgICBzcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5CiAgKSkKfQoKaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQppbnN0YWxsLnBhY2thZ2VzKCJwUk9DIikKbGlicmFyeShjYXJldCkKbGlicmFyeShwUk9DKQpgYGAKVGhlIGNvZGUgZW5jYXBzdWxhdGVzIGEgZnVuY3Rpb24gdGhhdCBmYWNpbGl0YXRlcyB0aGUgZXZhbHVhdGlvbiBvZiBjbGFzc2lmaWNhdGlvbiBtb2RlbHMuIFRoZSBmdW5jdGlvbiBjYWxjdWxhdGVzIGFuZCBwcmludHMgY29tbW9uIGNsYXNzaWZpY2F0aW9uIG1ldHJpY3MgKGFjY3VyYWN5LCBwcmVjaXNpb24sIHNlbnNpdGl2aXR5LCBhbmQgc3BlY2lmaWNpdHkpIGJhc2VkIG9uIHByZWRpY3RlZCBhbmQgYWN0dWFsIGxhYmVscy4gCgojIyMgRGVjaXNpb24gVHJlZXMKUHJlc2VudGVkIGFyZSB0d28gdHJlZSBtb2RlbHM6IHNpbXBsaWZpZWQgYW5kIG9yaWdpbmFsLiBJbiBkZWNpc2lvbiB0cmVlIG1vZGVscywgdGhlIGF0dHJpYnV0ZSBkZW1vbnN0cmF0aW5nIHRoZSBtb3N0IHNpZ25pZmljYW50IGluZm9ybWF0aW9uIGdhaW4gaXMgcmVwcmVzZW50ZWQgYXMgdGhlIGRlY2lzaW9uIG5vZGUgb3IgdGhlIHJvb3QuIFVwb24gaW5zcGVjdGlvbiBvZiB0aGUgZGlzcGxheWVkIHRyZWVzLCBpdOKAmXMgYXBwYXJlbnQgdGhhdCB0aGUg4oCYcmVsYXRpb25zaGlwc+KAmSBhdHRyaWJ1dGUgcG9zc2Vzc2VzIHRoZSBoaWdoZXN0IGluZm9ybWF0aW9uIGdhaW4sIHBvc2l0aW9uZWQgcHJvbWluZW50bHkgYXQgdGhlIHRvcCBvZiB0aGUgdHJlZSBzdHJ1Y3R1cmUuCgoKIyMgMS1GaXJzdCBQYXJ0aXRpb246ICg3MCUgdHJhaW5pbmcsIDMwJSB0ZXN0aW5nKQoKIyMjIFBhcnRpdGlvbmluZyAKYGBge3J9CnNldC5zZWVkKDEyMzQpCmluZD1zYW1wbGUoMiwgbnJvdyhiYWxhbmNlZF9wcmVwcm9jZXNzZWRfZGF0YXNldCksIHJlcGxhY2U9VFJVRSwgcHJvYj1jKDAuNzAgLCAwLjMwKSkKdHJhaW5fZGF0YV83MD1iYWxhbmNlZF9wcmVwcm9jZXNzZWRfZGF0YXNldFtpbmQ9PTEsXQp0ZXN0X2RhdGFfNzA9YmFsYW5jZWRfcHJlcHJvY2Vzc2VkX2RhdGFzZXRbaW5kPT0yLF0KYGBgClRoZSBjb2RlIHNwbGl0cyB0aGUgcHJlcHJvY2Vzc2VkIGRhdGFzZXQgaW50byA3MCUgdHJhaW5pbmcgZGF0YSBhbmQgMzAlIHRlc3RpbmcgZGF0YS4gCgojIyMgVmVyaWZ5IFBhcnRpdGlvbmluZwpgYGB7cn0KZGltKHRyYWluX2RhdGFfNzApCmRpbSh0ZXN0X2RhdGFfNzApCmBgYApUaGUgdHJhaW5pbmcgZGF0YSBjb25zaXN0IG9mIDM4MSByb3dzLiAKVGhlIHRlc3RpbmcgZGF0YSBjb25zaXN0IG9mIDE2OSByb3dzLgoKCiMjIEEpIElEMyBBbGdvcml0aG06IEluZm9ybWF0aW9uIEdhaW4gCgojIyMgQS4xKSBDb25zdHJ1Y3RpbmcgdGhlIERlY2lzaW9uIFRyZWUgCmBgYHtyfQpsaWJyYXJ5KHBhcnR5KQpsaWJyYXJ5KG12dG5vcm0pCmxpYnJhcnkobW9kZWx0b29scykKbGlicmFyeShzdGF0czQpCmxpYnJhcnkoc3RydWNjaGFuZ2UpCmxpYnJhcnkoem9vKQoKbXlGb3JtdWxhIDwtIHN0YXR1c34gc3RhdGVfY29kZSArIGNpdHkgKyBuYW1lICsgZm91bmRlZF9hdCArIGNsb3NlZF9hdCArIGZpcnN0X2Z1bmRpbmdfYXQgKyBsYXN0X2Z1bmRpbmdfYXQgKyAKICAgICAgICAgICAgYWdlX2ZpcnN0X2Z1bmRpbmdfeWVhciArIGFnZV9sYXN0X2Z1bmRpbmdfeWVhciArIGFnZV9maXJzdF9taWxlc3RvbmVfeWVhciArIGFnZV9sYXN0X21pbGVzdG9uZV95ZWFyICsgCiAgICAgICAgICAgIHJlbGF0aW9uc2hpcHMgKyBmdW5kaW5nX3JvdW5kcyArIGZ1bmRpbmdfdG90YWxfdXNkICsgbWlsZXN0b25lcyArIGlzX0NBICsgaXNfTlkgKyBpc19NQSArIGlzX1RYICsgaXNfb3RoZXJzdGF0ZSArIAogICAgICAgICAgICBjYXRlZ29yeV9jb2RlICsgaXNfc29mdHdhcmUgKyBpc193ZWIgKyBpc19tb2JpbGUgKyBpc19lbnRlcnByaXNlICsgaXNfYWR2ZXJ0aXNpbmcgKyBpc19nYW1lc3ZpZGVvICsgaXNfZWNvbW1lcmNlICsgCiAgICAgICAgICAgIGlzX2Jpb3RlY2ggKyBpc19jb25zdWx0aW5nICsgaXNfb3RoZXJjYXRlZ29yeSArIGhhc19WQyArIGhhc19hbmdlbCArIGhhc19yb3VuZEEgKyBoYXNfcm91bmRCICsgaGFzX3JvdW5kQyArIAogICAgICAgICAgICBoYXNfcm91bmREICsgYXZnX3BhcnRpY2lwYW50cyArIGlzX3RvcDUwMAoKcHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfSUdfNzA8LWN0cmVlKG15Rm9ybXVsYSwgZGF0YT10cmFpbl9kYXRhXzcwKQoKdGFibGUocHJlZGljdChwcmVwcm9jZXNzZWRfZGF0YXNldF9jdHJlZV9JR183MCksIHRyYWluX2RhdGFfNzAkc3RhdHVzKQpgYGAKVGhlIGNvZGUgYnVpbGRzIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsLiAKCiMjIyBBLjIpIFBsb3R0aW5nCmBgYHtyfQpwcmludChwcmVwcm9jZXNzZWRfZGF0YXNldF9jdHJlZV9JR183MCkKcGxvdChwcmVwcm9jZXNzZWRfZGF0YXNldF9jdHJlZV9JR183MCkKcGxvdChwcmVwcm9jZXNzZWRfZGF0YXNldF9jdHJlZV9JR183MCx0eXBlPSJzaW1wbGUiKQpgYGAKKipSb290IE5vZGUgKE5vZGUgMSk6Kio8YnI+CiZuYnNwOyZuYnNwO+KAoiBTcGxpdHRpbmcgYXR0cmlidXRlOiDigJxyZWxhdGlvbnNoaXBz4oCdIDw9IDMuPGJyPgombmJzcDsmbmJzcDvigKIgVGhlIGFsZ29yaXRobSBjb25zaWRlcnMgdGhlIOKAnHJlbGF0aW9uc2hpcHPigJ0gZmVhdHVyZSBmb3IgdGhlIGZpcnN0IGRlY2lzaW9uLjxicj4KCioqQnJhbmNoZXM6Kio8YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDI6IElmIOKAnHJlbGF0aW9uc2hpcHPigJ0gPD0gMywgaXQgZXZhbHVhdGVzIHRoZSBuZXh0IGNyaXRlcmlvbi48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDc6IElmIOKAnHJlbGF0aW9uc2hpcHPigJ0gPiAzLCBpdCBldmFsdWF0ZXMgYSBkaWZmZXJlbnQgY3JpdGVyaW9uLjxicj4KCioqTGVhZiBOb2RlcyAoVGVybWluYWwgTm9kZXMpOioqPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA0LCBOb2RlIDUsIE5vZGUgNiwgTm9kZSA5LCBOb2RlIDEwLCBOb2RlIDExOiBUZXJtaW5hbCBub2RlcyB3aGVyZSB0aGUgZGVjaXNpb24gdHJlZSBtYWtlcyBwcmVkaWN0aW9ucy48YnI+CgoqKk51bWJlciBvZiBOb2RlczoqKiA3ICgxIHJvb3QsIDQgaW50ZXJuYWwsIDIgbGVhZik8YnI+CgoqKk1vc3QgSW1wb3J0YW50IEZlYXR1cmVzOioqPGJyPgombmJzcDsmbmJzcDsxLiByZWxhdGlvbnNoaXBzOiBDcml0aWNhbCBhcyB0aGUgcm9vdCBub2RlLCBpbmRpY2F0aW5nIGhpZ2ggaW1wb3J0YW5jZS48YnI+CiZuYnNwOyZuYnNwOzIuIGlzX3RvcDUwMDogU2lnbmlmaWNhbnQgZm9yIGZ1cnRoZXIgc3BsaXRzLCBzaG93Y2FzaW5nIGl0cyByZWxldmFuY2UuPGJyPgombmJzcDsmbmJzcDszLiBhZ2VfbGFzdF9taWxlc3RvbmVfeWVhcjogVXNlZCBmb3IgYWRkaXRpb25hbCBzcGxpdHMsIGVtcGhhc2l6aW5nIGl0cyBjb250cmlidXRpb24uPGJyPgoKSW4gc3VtbWFyeSwgdGhpcyB0cmVlIHV0aWxpemVzIHRoZSDigJxyZWxhdGlvbnNoaXBz4oCdIGZlYXR1cmUgZm9yIHRoZSBpbml0aWFsIHNwbGl0LCBmb2xsb3dlZCBieSBhZGRpdGlvbmFsIGNyaXRlcmlhIGF0IGVhY2ggYnJhbmNoaW5nIHBvaW50LCB1bHRpbWF0ZWx5IGxlYWRpbmcgdG8gcHJlZGljdGlvbnMgYXQgdGhlIHRlcm1pbmFsIG5vZGVzLiBUaGUgd2VpZ2h0cyBhc3NvY2lhdGVkIHdpdGggZWFjaCB0ZXJtaW5hbCBub2RlIGluZGljYXRlIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZhbGxpbmcgaW50byBlYWNoIGNhdGVnb3J5LgoKIyMjIEEuMykgVGVzdGluZyAKYGBge3J9CnByZWRpY3Rpb25zX0lHXzcwIDwtIHByZWRpY3QocHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfSUdfNzAsIG5ld2RhdGEgPSB0ZXN0X2RhdGFfNzAsIHR5cGUgPSAicmVzcG9uc2UiKQpsYWJlbHNfSUdfNzAgPC0gdGVzdF9kYXRhXzcwJHN0YXR1cwpgYGAKVGhlIGNvZGUgZ2VuZXJhdGVzIHByZWRpY3Rpb25zIGZvciB0aGUgdGVzdCBkYXRhc2V0IHVzaW5nIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsLgoKCiMjIyBBLjQpIENvbmZ1c2lvbiBNYXRyaXggCmBgYHtyfQpjb25mdXNpb25fbWF0cml4X0lHXzcwIDwtIHRhYmxlKHRlc3RfZGF0YV83MCRzdGF0dXMsIHByZWRpY3Rpb25zX0lHXzcwKQpwcmludChjb25mdXNpb25fbWF0cml4X0lHXzcwKQpgYGAKVGhlIGNvbmZ1c2lvbiBtYXRyaXggZm9yIHRoZSBwcm92aWRlZCBjb2RlIGluZGljYXRlcyB0aGUgZm9sbG93aW5nOjxicj4KCi0gKipUcnVlIFBvc2l0aXZlIChUUCk6KiogNDggY2FzZXMgd2VyZSBjb3JyZWN0bHkgcHJlZGljdGVkIGFzIDAuNzc1Ljxicj4KLSAqKkZhbHNlIFBvc2l0aXZlIChGUCk6KiogMzUgY2FzZXMgd2VyZSBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgYXMgMC41MjUsIDYgY2FzZXMgYXMgMC43NzUsIGFuZCAyMyBjYXNlcyBhcyAwLjI2MzIuPGJyPgotICoqVHJ1ZSBOZWdhdGl2ZSAoVE4pOioqIDEyIGNhc2VzIHdlcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjI2MzIsIDcgY2FzZXMgYXMgMC41MjUsIGFuZCAzOCBjYXNlcyBhcyAwLjc3NS48YnI+Ci0gKipGYWxzZSBOZWdhdGl2ZSAoRk4pOioqIDAgY2FzZXMgd2VyZSBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgYXMgMC4yNjMyLjxicj4KCiMjIyBBLjUpIEV2YWx1YXRpb24gTWV0cmljcwoKYGBge3J9Cm1ldHJpY3NfSUdfNzAgPC0gZXZhbHVhdGVfbW9kZWwocHJlZGljdGlvbnNfSUdfNzAsIGxhYmVsc19JR183MCkKcHJpbnQobWV0cmljc19JR183MCkKYGBgCuKAogkqKkFjY3VyYWN5ICgxMS4yJSk6KiogVGhpcyBtZXRyaWMgcmVwcmVzZW50cyB0aGUgb3ZlcmFsbCBjb3JyZWN0bmVzcyBvZiB0aGUgbW9kZWzigJlzIHByZWRpY3Rpb25zLiBJbiB0aGlzIGNhc2UsIHRoZSBtb2RlbCBhY2hpZXZlZCBhbiBhY2N1cmFjeSBvZiAxNS40JSwgaW5kaWNhdGluZyB0aGF0IG9ubHkgYSBzbWFsbCBwZXJjZW50YWdlIG9mIHByZWRpY3Rpb25zIHdlcmUgY29ycmVjdC48YnI+CuKAogkqKlByZWNpc2lvbiAoMTAwICUpOioqIFByZWNpc2lvbiBtZWFzdXJlcyB0aGUgYWNjdXJhY3kgb2YgcG9zaXRpdmUgcHJlZGljdGlvbnMuIEluIHRoaXMgY29udGV4dCwgdGhlIG1vZGVsIGFjaGlldmVkIGEgcHJlY2lzaW9uIG9mIDg0LjIlLCBpbmRpY2F0aW5nIHRoYXQgd2hlbiBpdCBwcmVkaWN0ZWQgYSBwb3NpdGl2ZSBvdXRjb21lLCBpdCB3YXMgY29ycmVjdCBpbiA4NC4yJSBvZiBjYXNlcy48YnI+CuKAogkqKlNlbnNpdGl2aXR5ICgzNC4yICUpOioqIFNlbnNpdGl2aXR5LCBhbHNvIGtub3duIGFzIHJlY2FsbCwgbWVhc3VyZXMgdGhlIGFiaWxpdHkgb2YgdGhlIG1vZGVsIHRvIGNvcnJlY3RseSBpZGVudGlmeSBwb3NpdGl2ZSBpbnN0YW5jZXMuIEluIHRoaXMgY2FzZSwgdGhlIG1vZGVs4oCZcyBzZW5zaXRpdml0eSBpcyAzMiUsIHN1Z2dlc3RpbmcgdGhhdCBpdCBkaWRu4oCZdCBwZXJmb3JtIHdlbGwgaW4gY2FwdHVyaW5nIGFsbCBwb3NpdGl2ZSBpbnN0YW5jZXMuPGJyPgrigKIJKipTcGVjaWZpY2l0eSAoMTAwICUpOioqIFNwZWNpZmljaXR5IG1lYXN1cmVzIHRoZSBhYmlsaXR5IG9mIHRoZSBtb2RlbCB0byBjb3JyZWN0bHkgaWRlbnRpZnkgbmVnYXRpdmUgaW5zdGFuY2VzLiBXaXRoIGEgc3BlY2lmaWNpdHkgb2YgNzYuOSUsIHRoZSBtb2RlbCBwZXJmb3JtZWQgcmVsYXRpdmVseSB3ZWxsIGluIGFjY3VyYXRlbHkgaWRlbnRpZnlpbmcgbmVnYXRpdmUgY2FzZXMuPGJyPgoKSW4gc3VtbWFyeSwgd2hpbGUgdGhlIG1vZGVsIGV4aGliaXRlZCBoaWdoIHByZWNpc2lvbiBhbmQgc3BlY2lmaWNpdHksIHRoZSBsb3cgYWNjdXJhY3kgYW5kIHNlbnNpdGl2aXR5IHNpZ25hbCBwb3RlbnRpYWwgY2hhbGxlbmdlcywgZXNwZWNpYWxseSBpbiBjb3JyZWN0bHkgaWRlbnRpZnlpbmcgcG9zaXRpdmUgaW5zdGFuY2VzLiAKCiMjIyBCKSBDNTAgQWxnb3JpdGhtOiBHYWluIFJhdGlvCgojIyMgQi4xKSBDb25zdHJ1Y3RpbmcgdGhlIERlY2lzaW9uIFRyZWUgCgpgYGB7cn0KbGlicmFyeShDNTApCgojIENvbnZlcnQgJ3N0YXR1cycgdG8gYSBmYWN0b3IKdHJhaW5fZGF0YV83MCRzdGF0dXMgPC0gYXMuZmFjdG9yKHRyYWluX2RhdGFfNzAkc3RhdHVzKQoKIyBUcmFpbiB0aGUgbW9kZWwgdXNpbmcgQzUuMCB3aXRoIEdhaW4gUmF0aW8gZm9yIHNwbGl0dGluZwpDNUZpdF83MCA8LSBDNS4wKHN0YXR1cyB+IC4sIGRhdGEgPSB0cmFpbl9kYXRhXzcwLCBjb250cm9sID0gQzUuMENvbnRyb2woZWFybHlTdG9wcGluZyA9IEZBTFNFLCBDRiA9IDAuMjUpKQoKIyBQcmludCB0aGUgc3VtbWFyeSBvZiB0aGUgbW9kZWwKc3VtbWFyeShDNUZpdF83MCkKYGBgClRoZSBjb2RlIGJ1aWxkcyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbC4gCgoKIyMjIEIuMikgUGxvdHRpbmcKCmBgYHtyfQpwbG90KEM1Rml0XzcwLCB0eXBlPSJzaW1wbGUiKQpgYGAKKipSb290IE5vZGUgKE5vZGUgMSk6Kio8YnI+CiZuYnNwOyZuYnNwO+KAoiBTcGxpdHRpbmcgYXR0cmlidXRlOiDigJxyZWxhdGlvbnNoaXBz4oCdIDw9IDIuPGJyPgombmJzcDsmbmJzcDvigKIgVGhlIGFsZ29yaXRobSBjb25zaWRlcnMgdGhlIOKAnHJlbGF0aW9uc2hpcHPigJ0gZmVhdHVyZSBmb3IgdGhlIGZpcnN0IGRlY2lzaW9uLjxicj4KCioqQnJhbmNoZXM6Kio8YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDI6IElmIOKAnHJlbGF0aW9uc2hpcHPigJ0gPD0gMiwgaXQgZXZhbHVhdGVzIHRoZSBuZXh0IGNyaXRlcmlvbi48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDc6IElmIOKAnHJlbGF0aW9uc2hpcHPigJ0gPiAyLCBpdCBldmFsdWF0ZXMgYSBkaWZmZXJlbnQgY3JpdGVyaW9uLjxicj4KCioqTGVhZiBOb2RlcyAoVGVybWluYWwgTm9kZXMpOioqPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA0LCBOb2RlIDUsIE5vZGUgNiwgTm9kZSA5LCBOb2RlIDEwLCBOb2RlIDExOiBUZXJtaW5hbCBub2RlcyB3aGVyZSB0aGUgZGVjaXNpb24gdHJlZSBtYWtlcyBwcmVkaWN0aW9ucy48YnI+CgoqKk51bWJlciBvZiBOb2RlczoqKiA1MyAoMSByb290LCAyNiBpbnRlcm5hbCwgMjYgbGVhZik8YnI+CgoqKk1vc3QgSW1wb3J0YW50IEZlYXR1cmVzOioqPGJyPgombmJzcDsmbmJzcDsxLiByZWxhdGlvbnNoaXBzOiBDcml0aWNhbCBhcyB0aGUgcm9vdCBub2RlLCBpbmRpY2F0aW5nIGhpZ2ggaW1wb3J0YW5jZSAoMTAwLjAwJSkuPGJyPgombmJzcDsmbmJzcDsyLiBmdW5kaW5nX3RvdGFsX3VzZDogU2lnbmlmaWNhbnQgZm9yIGZ1cnRoZXIgc3BsaXRzLCBzaG93Y2FzaW5nIGl0cyByZWxldmFuY2UgKDczLjQ5JSkuPGJyPgombmJzcDsmbmJzcDszLiBoYXNfcm91bmREOiBQbGF5cyBhIGtleSByb2xlIGluIGRlY2lzaW9uLW1ha2luZyAoNzAuMzQlKS48YnI+CgpJbiBzdW1tYXJ5LCB0aGlzIHRyZWUgdXRpbGl6ZXMgdGhlIOKAnHJlbGF0aW9uc2hpcHPigJ0gZmVhdHVyZSBmb3IgdGhlIGluaXRpYWwgc3BsaXQsIGZvbGxvd2VkIGJ5IGFkZGl0aW9uYWwgY3JpdGVyaWEgYXQgZWFjaCBicmFuY2hpbmcgcG9pbnQsIHVsdGltYXRlbHkgbGVhZGluZyB0byBwcmVkaWN0aW9ucyBhdCB0aGUgdGVybWluYWwgbm9kZXMuIFRoZSB3ZWlnaHRzIGFzc29jaWF0ZWQgd2l0aCBlYWNoIHRlcm1pbmFsIG5vZGUgaW5kaWNhdGUgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgZmFsbGluZyBpbnRvIGVhY2ggY2F0ZWdvcnkuIFRoZSBtb3N0IGltcG9ydGFudCBmZWF0dXJlcywgYXMgaWRlbnRpZmllZCBieSB0aGUgYXR0cmlidXRlIHVzYWdlLCBpbmNsdWRlIOKAnHJlbGF0aW9uc2hpcHMs4oCdIOKAnGZ1bmRpbmdfdG90YWxfdXNkLOKAnSBhbmQg4oCcaGFzX3JvdW5kRC7igJ0KCiMjIyBCLjMpIFRlc3RpbmcgCgpgYGB7cn0KcHJlZGljdGlvbnNfR1JfNzAgPC0gcHJlZGljdChDNUZpdF83MCwgbmV3ZGF0YSA9IHRlc3RfZGF0YV83MCkKbGFiZWxzX0dSXzcwIDwtIHRlc3RfZGF0YV83MCRzdGF0dXMKYGBgClRoZSBjb2RlIGdlbmVyYXRlcyBwcmVkaWN0aW9ucyBmb3IgdGhlIHRlc3QgZGF0YXNldCB1c2luZyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbC4KCiMjIyBCLjQpIENvbmZ1c2lvbiBNYXRyaXggCgpgYGB7cn0KIyBDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4CmNvbmZ1c2lvbl9tYXRyaXhfR1JfNzAgPC0gdGFibGUodGVzdF9kYXRhXzcwJHN0YXR1cywgcHJlZGljdGlvbnNfR1JfNzApCgojIERpc3BsYXkgdGhlIGNvbmZ1c2lvbiBtYXRyaXgKcHJpbnQoY29uZnVzaW9uX21hdHJpeF9HUl83MCkKYGBgClRoZSBjb25mdXNpb24gbWF0cml4IGZvciB0aGUgcHJvdmlkZWQgY29kZSBpbmRpY2F0ZXMgdGhlIGZvbGxvd2luZzo8YnI+CgoJ4oCiCVRydWUgUG9zaXRpdmUgKFRQKTogNjYgY2FzZXMgd2VyZSBjb3JyZWN0bHkgcHJlZGljdGVkIGFzIDEuPGJyPgoJ4oCiCUZhbHNlIFBvc2l0aXZlIChGUCk6IDI4IGNhc2VzIHdlcmUgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIDEsIGFuZCAyNyBjYXNlcyB3ZXJlIGluY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjxicj4KCeKAoglUcnVlIE5lZ2F0aXZlIChUTik6IDQ4IGNhc2VzIHdlcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjxicj4KCeKAoglGYWxzZSBOZWdhdGl2ZSAoRk4pOiAyNyBjYXNlcyB3ZXJlIGluY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjxicj4KCiMjIyBCLjUpIEV2YWx1YXRpb24gTWV0cmljcwoKYGBge3J9Cm1ldHJpY3NfR1JfNzAgPC0gZXZhbHVhdGVfbW9kZWwocHJlZGljdGlvbnNfR1JfNzAsIGxhYmVsc19HUl83MCkKYGBgCuKAoiBBY2N1cmFjeSAoNjcuNDYlKTogVGhpcyBtZXRyaWMgcmVwcmVzZW50cyB0aGUgb3ZlcmFsbCBjb3JyZWN0bmVzcyBvZiB0aGUgbW9kZWzigJlzIHByZWRpY3Rpb25zLiBJbiB0aGlzIGNhc2UsIHRoZSBtb2RlbCBhY2hpZXZlZCBhbiBhY2N1cmFjeSBvZiA2Ny40NiUsIGluZGljYXRpbmcgYSByZWxhdGl2ZWx5IG1vZGVyYXRlIGxldmVsIG9mIGNvcnJlY3RuZXNzLjxicj4KCuKAoiBQcmVjaXNpb24gKDY0JSk6IFByZWNpc2lvbiBtZWFzdXJlcyB0aGUgYWNjdXJhY3kgb2YgcG9zaXRpdmUgcHJlZGljdGlvbnMuIEluIHRoaXMgY29udGV4dCwgdGhlIG1vZGVsIGFjaGlldmVkIGEgcHJlY2lzaW9uIG9mIDY0JSwgaW5kaWNhdGluZyB0aGF0IHdoZW4gaXQgcHJlZGljdGVkIGEgcG9zaXRpdmUgb3V0Y29tZSwgaXQgd2FzIGNvcnJlY3QgaW4gNjQlIG9mIGNhc2VzLjxicj4KCuKAoiBTZW5zaXRpdml0eSAoUmVjYWxsKSAoNjMuMTYlKTogU2Vuc2l0aXZpdHksIGFsc28ga25vd24gYXMgcmVjYWxsLCBtZWFzdXJlcyB0aGUgYWJpbGl0eSBvZiB0aGUgbW9kZWwgdG8gY29ycmVjdGx5IGlkZW50aWZ5IHBvc2l0aXZlIGluc3RhbmNlcy4gSW4gdGhpcyBjYXNlLCB0aGUgbW9kZWzigJlzIHNlbnNpdGl2aXR5IGlzIDYzLjE2JSwgc3VnZ2VzdGluZyBhIG1vZGVyYXRlIHBlcmZvcm1hbmNlIGluIGNhcHR1cmluZyBwb3NpdGl2ZSBpbnN0YW5jZXMuPGJyPgoK4oCiIFNwZWNpZmljaXR5ICg3MC45NyUpOiBTcGVjaWZpY2l0eSBtZWFzdXJlcyB0aGUgYWJpbGl0eSBvZiB0aGUgbW9kZWwgdG8gY29ycmVjdGx5IGlkZW50aWZ5IG5lZ2F0aXZlIGluc3RhbmNlcy4gV2l0aCBhIHNwZWNpZmljaXR5IG9mIDcwLjk3JSwgdGhlIG1vZGVsIHBlcmZvcm1lZCByZWxhdGl2ZWx5IHdlbGwgaW4gYWNjdXJhdGVseSBpZGVudGlmeWluZyBuZWdhdGl2ZSBjYXNlcy48YnI+CgpJbiBzdW1tYXJ5LCB3aGlsZSB0aGUgbW9kZWwgZXhoaWJpdGVkIG1vZGVyYXRlIGFjY3VyYWN5IGFuZCBzZW5zaXRpdml0eSwgdGhlIHByZWNpc2lvbiBhbmQgc3BlY2lmaWNpdHkgc3VnZ2VzdCBhIHJlYXNvbmFibGUgYWJpbGl0eSB0byBtYWtlIGNvcnJlY3QgcHJlZGljdGlvbnMsIGVzcGVjaWFsbHkgaW4gaWRlbnRpZnlpbmcgbmVnYXRpdmUgaW5zdGFuY2VzLjxicj4KCiMjIyBDQVJUIEFsZ29yaXRobTogR2luaSBJbmRleCAKCiMjIyBDLjEpIENvbnN0cnVjdGluZyB0aGUgRGVjaXNpb24gVHJlZSAKYGBge3J9CmxpYnJhcnkocnBhcnQpCiMgR2luaSBpbmRleCAoQ0FSVCkgYW5kIFRyZWUgbW9kZWwgNzA6MzAKcHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfR0lfNzAgPC0gcnBhcnQoc3RhdHVzIH4gLiwgZGF0YSA9IHRyYWluX2RhdGFfNzAsIG1ldGhvZCA9ICJjbGFzcyIsIHBhcm1zID0gbGlzdChzcGxpdCA9ICJnaW5pIikpCmBgYApUaGUgY29kZSBidWlsZHMgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwuIAoKIyMjIEMuMikgUGxvdHRpbmcKYGBge3J9CmxpYnJhcnkocnBhcnQucGxvdCkKcHJpbnQocHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfR0lfNzApCnJwYXJ0LnBsb3QocHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfR0lfNzApCmBgYAoKCioqUm9vdCBOb2RlIChOb2RlIDEpOioqPGJyPgombmJzcDsmbmJzcDvigKIgU3BsaXR0aW5nIGF0dHJpYnV0ZTogInJlbGF0aW9uc2hpcHMiIDwgMi41LCBjb25zaWRlcmluZyAzODEgb2JzZXJ2YXRpb25zIHdpdGggYW4gZXhwZWN0ZWQgbG9zcyBvZiAwLjQ3NzY5MDMgKDUyLjIlIGNsYXNzIDAsIDQ3LjglIGNsYXNzIDEpLjxicj4KCioqQnJhbmNoZXM6Kio8YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDI6IElmICJyZWxhdGlvbnNoaXBzIiA8IDIuNSwgaXQgcHJlZGljdHMgY2xhc3MgMCB3aXRoIGFuIGV4cGVjdGVkIGxvc3Mgb2YgMC4xNDg1MTQ5ICg4NS4xJSBjbGFzcyAwLCAxNC45JSBjbGFzcyAxKS48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDM6IElmICJyZWxhdGlvbnNoaXBzIiA+PSAyLjUsIGl0IHByZWRpY3RzIGNsYXNzIDEgd2l0aCBhbiBleHBlY3RlZCBsb3NzIG9mIDAuNDAzNTcxNCAoNDAuNCUgY2xhc3MgMCwgNTkuNiUgY2xhc3MgMSkuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA2OiBJZiAiZnVuZGluZ190b3RhbF91c2QiIDwgMC4wMDcyMzQwMiAodW5kZXIgTm9kZSAzKSwgaXQgcHJlZGljdHMgY2xhc3MgMCB3aXRoIGFuIGV4cGVjdGVkIGxvc3Mgb2YgMC4wOTUyMzgxICg5MC41JSBjbGFzcyAwLCA5LjUlIGNsYXNzIDEpLjxicj4KJm5ic3A7Jm5ic3A74oCiIE5vZGUgNzogSWYgImZ1bmRpbmdfdG90YWxfdXNkIiA+PSAwLjAwNzIzNDAyICh1bmRlciBOb2RlIDMpLCBpdCBwcmVkaWN0cyBjbGFzcyAxIHdpdGggYW4gZXhwZWN0ZWQgbG9zcyBvZiAwLjM2MjkzNDQgKDM2LjMlIGNsYXNzIDAsIDYzLjclIGNsYXNzIDEpLjxicj4KJm5ic3A7Jm5ic3A74oCiIE5vZGUgMTQ6IElmICJtaWxlc3RvbmVzIiA8IDAuNSAodW5kZXIgTm9kZSA3KSwgaXQgcHJlZGljdHMgY2xhc3MgMCB3aXRoIGFuIGV4cGVjdGVkIGxvc3Mgb2YgMC4yODU3MTQzICg3MS40JSBjbGFzcyAwLCAyOC42JSBjbGFzcyAxKS48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDE1OiBJZiAibWlsZXN0b25lcyIgPj0gMC41ICh1bmRlciBOb2RlIDcpLCBpdCBwcmVkaWN0cyBjbGFzcyAxIHdpdGggYW4gZXhwZWN0ZWQgbG9zcyBvZiAwLjMyMDM0NjMgKDMyLjAlIGNsYXNzIDAsIDY4LjAlIGNsYXNzIDEpLjxicj4KCioqTGVhZiBOb2RlcyAoVGVybWluYWwgTm9kZXMpOioqPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSAyODogVGVybWluYWwgbm9kZSBwcmVkaWN0aW5nIGNsYXNzIDAgKDEwMCUgY2xhc3MgMCwgMCUgY2xhc3MgMSkuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSAyOTogVGVybWluYWwgbm9kZSBwcmVkaWN0aW5nIGNsYXNzIDEgKDQyLjklIGNsYXNzIDAsIDU3LjElIGNsYXNzIDEpLjxicj4KCioqTnVtYmVyIG9mIE5vZGVzOioqIDggKDEgcm9vdCwgNiBpbnRlcm5hbCwgMiBsZWFmKTxicj4KCioqTW9zdCBJbXBvcnRhbnQgRmVhdHVyZXM6Kio8YnI+CiZuYnNwOyZuYnNwOzEuIHJlbGF0aW9uc2hpcHM6IENyaXRpY2FsIGFzIHRoZSByb290IG5vZGUsIGluZGljYXRpbmcgaGlnaCBpbXBvcnRhbmNlLjxicj4KJm5ic3A7Jm5ic3A7Mi4gZnVuZGluZ190b3RhbF91c2Q6IFNpZ25pZmljYW50IGZvciBmdXJ0aGVyIHNwbGl0cywgc2hvd2Nhc2luZyBpdHMgcmVsZXZhbmNlLjxicj4KJm5ic3A7Jm5ic3A7My4gbWlsZXN0b25lczogVXNlZCBmb3IgYWRkaXRpb25hbCBzcGxpdHMsIGVtcGhhc2l6aW5nIGl0cyBjb250cmlidXRpb24uPGJyPgoKSW4gc3VtbWFyeSwgdGhpcyB0cmVlIHV0aWxpemVzIHRoZSAicmVsYXRpb25zaGlwcyIgZmVhdHVyZSBmb3IgdGhlIGluaXRpYWwgc3BsaXQsIGZvbGxvd2VkIGJ5IGFkZGl0aW9uYWwgY3JpdGVyaWEgYXQgZWFjaCBicmFuY2hpbmcgcG9pbnQsIHVsdGltYXRlbHkgbGVhZGluZyB0byBwcmVkaWN0aW9ucyBhdCB0aGUgdGVybWluYWwgbm9kZXMuIFRoZSB3ZWlnaHRzIGFzc29jaWF0ZWQgd2l0aCBlYWNoIHRlcm1pbmFsIG5vZGUgaW5kaWNhdGUgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgZmFsbGluZyBpbnRvIGVhY2ggY2F0ZWdvcnkuCiMjIyBDLjMpIFRlc3RpbmcgCmBgYHtyfQpwcmVkaWN0aW9uc19HSV83MCA8LSBwcmVkaWN0KHByZXByb2Nlc3NlZF9kYXRhc2V0X2N0cmVlX0dJXzcwLCBuZXdkYXRhID0gdGVzdF9kYXRhXzcwLCB0eXBlID0gImNsYXNzIikKbGFiZWxzX0dJXzcwIDwtIHRlc3RfZGF0YV83MCRzdGF0dXMKYGBgClRoZSBjb2RlIGdlbmVyYXRlcyBwcmVkaWN0aW9ucyBmb3IgdGhlIHRlc3QgZGF0YXNldCB1c2luZyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbC4KCiMjIyBDLjQpIENvbmZ1c2lvbiBNYXRyaXgKYGBge3J9CiMgQ3JlYXRlIGEgY29uZnVzaW9uIG1hdHJpeApjb25mdXNpb25fbWF0cml4X0dJXzcwIDwtIHRhYmxlKHRlc3RfZGF0YV83MCRzdGF0dXMsIHByZWRpY3Rpb25zX0dJXzcwKQoKIyBEaXNwbGF5IHRoZSBjb25mdXNpb24gbWF0cml4CnByaW50KGNvbmZ1c2lvbl9tYXRyaXhfR0lfNzApCmBgYApUaGUgY29uZnVzaW9uIG1hdHJpeCBmb3IgdGhlIHByb3ZpZGVkIGNvZGUgaW5kaWNhdGVzIHRoZSBmb2xsb3dpbmc6CgoJ4oCiCVRydWUgUG9zaXRpdmUgKFRQKTogODQgY2FzZXMgd2VyZSBjb3JyZWN0bHkgcHJlZGljdGVkIGFzIDEuPGJyPgoJ4oCiCUZhbHNlIFBvc2l0aXZlIChGUCk6IDI2IGNhc2VzIHdlcmUgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIDEsIGFuZCA5IGNhc2VzIHdlcmUgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIDAuPGJyPgoJ4oCiCVRydWUgTmVnYXRpdmUgKFROKTogNTAgY2FzZXMgd2VyZSBjb3JyZWN0bHkgcHJlZGljdGVkIGFzIDAuPGJyPgoJ4oCiCUZhbHNlIE5lZ2F0aXZlIChGTik6IDkgY2FzZXMgd2VyZSBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgYXMgMC48YnI+CgojIyMgQy41KSBFdmFsdWF0aW9uIE1ldHJpY3MKCmBgYHtyfQptZXRyaWNzX0dJXzcwIDwtIGV2YWx1YXRlX21vZGVsKHByZWRpY3Rpb25zX0dJXzcwLCBsYWJlbHNfR0lfNzApCmBgYArigKIgQWNjdXJhY3kgKDc5LjI5JSk6IFRoaXMgbWV0cmljIHJlcHJlc2VudHMgdGhlIG92ZXJhbGwgY29ycmVjdG5lc3Mgb2YgdGhlIG1vZGVs4oCZcyBwcmVkaWN0aW9ucy4gSW4gdGhpcyBjYXNlLCB0aGUgbW9kZWwgYWNoaWV2ZWQgYW4gYWNjdXJhY3kgb2YgNzkuMjklLCBpbmRpY2F0aW5nIGEgcmVsYXRpdmVseSBoaWdoIGxldmVsIG9mIGNvcnJlY3RuZXNzLjxicj4KCuKAoiBQcmVjaXNpb24gKDg0Ljc1JSk6IFByZWNpc2lvbiBtZWFzdXJlcyB0aGUgYWNjdXJhY3kgb2YgcG9zaXRpdmUgcHJlZGljdGlvbnMuIEluIHRoaXMgY29udGV4dCwgdGhlIG1vZGVsIGFjaGlldmVkIGEgcHJlY2lzaW9uIG9mIDg0Ljc1JSwgaW5kaWNhdGluZyB0aGF0IHdoZW4gaXQgcHJlZGljdGVkIGEgcG9zaXRpdmUgb3V0Y29tZSwgaXQgd2FzIGNvcnJlY3QgaW4gODQuNzUlIG9mIGNhc2VzLjxicj4KCuKAoiBTZW5zaXRpdml0eSAoUmVjYWxsKSAoNjUuNzklKTogU2Vuc2l0aXZpdHksIGFsc28ga25vd24gYXMgcmVjYWxsLCBtZWFzdXJlcyB0aGUgYWJpbGl0eSBvZiB0aGUgbW9kZWwgdG8gY29ycmVjdGx5IGlkZW50aWZ5IHBvc2l0aXZlIGluc3RhbmNlcy4gSW4gdGhpcyBjYXNlLCB0aGUgbW9kZWzigJlzIHNlbnNpdGl2aXR5IGlzIDY1Ljc5JSwgc3VnZ2VzdGluZyBhIG1vZGVyYXRlIHBlcmZvcm1hbmNlIGluIGNhcHR1cmluZyBwb3NpdGl2ZSBpbnN0YW5jZXMuPGJyPgoK4oCiIFNwZWNpZmljaXR5ICg5MC4zMiUpOiBTcGVjaWZpY2l0eSBtZWFzdXJlcyB0aGUgYWJpbGl0eSBvZiB0aGUgbW9kZWwgdG8gY29ycmVjdGx5IGlkZW50aWZ5IG5lZ2F0aXZlIGluc3RhbmNlcy4gV2l0aCBhIHNwZWNpZmljaXR5IG9mIDkwLjMyJSwgdGhlIG1vZGVsIHBlcmZvcm1lZCB3ZWxsIGluIGFjY3VyYXRlbHkgaWRlbnRpZnlpbmcgbmVnYXRpdmUgY2FzZXMuPGJyPgoKSW4gc3VtbWFyeSwgdGhlIG1vZGVsIGV4aGliaXRlZCBoaWdoIGFjY3VyYWN5IGFuZCBwcmVjaXNpb24sIHdpdGggZ29vZCBzcGVjaWZpY2l0eS4gSG93ZXZlciwgdGhlIHNlbnNpdGl2aXR5IGluZGljYXRlcyB0aGF0IHRoZXJlIG1pZ2h0IGJlIHJvb20gZm9yIGltcHJvdmVtZW50IGluIGNhcHR1cmluZyBwb3NpdGl2ZSBpbnN0YW5jZXMuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgU2Vjb25kIFBhcnRpdGlvbjogICg4MCUgdHJhaW5pbmcsIDIwJSB0ZXN0aW5nKQoKIyMjIFBhcnRpdGlvbmluZyAKYGBge3J9CnNldC5zZWVkKDEyMzQpCmluZD1zYW1wbGUoMiwgbnJvdyhiYWxhbmNlZF9wcmVwcm9jZXNzZWRfZGF0YXNldCksIHJlcGxhY2U9VFJVRSwgcHJvYj1jKDAuODAgLCAwLjIwKSkKdHJhaW5fZGF0YV84MD1iYWxhbmNlZF9wcmVwcm9jZXNzZWRfZGF0YXNldFtpbmQ9PTEsXQp0ZXN0X2RhdGFfODA9YmFsYW5jZWRfcHJlcHJvY2Vzc2VkX2RhdGFzZXRbaW5kPT0yLF0KYGBgCgpUaGUgY29kZSBzcGxpdHMgdGhlIHByZXByb2Nlc3NlZCBkYXRhc2V0IGludG8gODAlIHRyYWluaW5nIGRhdGEgYW5kIDIwJSB0ZXN0aW5nIGRhdGEuIAoKCiMjIyMgVmVyaWZ5IFBhcnRpdGlvbmluZwpgYGB7cn0KZGltKHRyYWluX2RhdGFfODApCmRpbSh0ZXN0X2RhdGFfODApCmBgYApUaGUgdHJhaW5pbmcgZGF0YSBjb25zaXN0IG9mIDQ0MyByb3dzLiAKVGhlIHRlc3RpbmcgZGF0YSBjb25zaXN0IG9mIDEwNyByb3dzLgoKIyMjIEEuMSkgQ29uc3RydWN0aW5nIHRoZSBEZWNpc2lvbiBUcmVlCmBgYHtyfQpsaWJyYXJ5KHBhcnR5KQpteUZvcm11bGEgPC0gc3RhdHVzIH4gc3RhdGVfY29kZSArIGNpdHkgKyBuYW1lICsgZm91bmRlZF9hdCArIGNsb3NlZF9hdCArIGZpcnN0X2Z1bmRpbmdfYXQgKyBsYXN0X2Z1bmRpbmdfYXQgKyAKICAgICAgICAgICAgYWdlX2ZpcnN0X2Z1bmRpbmdfeWVhciArIGFnZV9sYXN0X2Z1bmRpbmdfeWVhciArIGFnZV9maXJzdF9taWxlc3RvbmVfeWVhciArIGFnZV9sYXN0X21pbGVzdG9uZV95ZWFyICsgCiAgICAgICAgICAgIHJlbGF0aW9uc2hpcHMgKyBmdW5kaW5nX3JvdW5kcyArIGZ1bmRpbmdfdG90YWxfdXNkICsgbWlsZXN0b25lcyArIGlzX0NBICsgaXNfTlkgKyBpc19NQSArIGlzX1RYICsgaXNfb3RoZXJzdGF0ZSArIAogICAgICAgICAgICBjYXRlZ29yeV9jb2RlICsgaXNfc29mdHdhcmUgKyBpc193ZWIgKyBpc19tb2JpbGUgKyBpc19lbnRlcnByaXNlICsgaXNfYWR2ZXJ0aXNpbmcgKyBpc19nYW1lc3ZpZGVvICsgaXNfZWNvbW1lcmNlICsgCiAgICAgICAgICAgIGlzX2Jpb3RlY2ggKyBpc19jb25zdWx0aW5nICsgaXNfb3RoZXJjYXRlZ29yeSArIGhhc19WQyArIGhhc19hbmdlbCArIGhhc19yb3VuZEEgKyBoYXNfcm91bmRCICsgaGFzX3JvdW5kQyArIAogICAgICAgICAgICBoYXNfcm91bmREICsgYXZnX3BhcnRpY2lwYW50cyArIGlzX3RvcDUwMApwcmVwcm9jZXNzZWRfZGF0YXNldF9jdHJlZV9JR184MCA8LSBjdHJlZShteUZvcm11bGEsIGRhdGEgPSB0cmFpbl9kYXRhXzgwKQoKdGFibGUocHJlZGljdChwcmVwcm9jZXNzZWRfZGF0YXNldF9jdHJlZV9JR184MCksIHRyYWluX2RhdGFfODAkc3RhdHVzKQpgYGAKVGhlIGNvZGUgYnVpbGRzIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsLiAKCgojIyMgQS4yKSBQbG90dGluZwpgYGB7cn0KcHJpbnQocHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfSUdfODApCnBsb3QocHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfSUdfODApCnBsb3QocHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfSUdfODAsIHR5cGUgPSAic2ltcGxlIikKYGBgCioqUm9vdCBOb2RlIChOb2RlIDEpOioqPGJyPgombmJzcDsmbmJzcDvigKIgU3BsaXR0aW5nIGF0dHJpYnV0ZTog4oCccmVsYXRpb25zaGlwc+KAnSA8IDMsIGNvbnNpZGVyaW5nIDQ0MyBvYnNlcnZhdGlvbnMgd2l0aCBhIGNyaXRlcmlvbiBvZiAxIGFuZCBhIHN0YXRpc3RpYyBvZiA3MC45MDUgKGV4cGVjdGVkIGxvc3M6IDcwLjkwNSUpLjxicj4KCioqQnJhbmNoZXM6Kio8YnI+CiZuYnNwOyZuYnNwO05vZGUgMjogSWYg4oCccmVsYXRpb25zaGlwc+KAnSA8PSAzLCBpdCBmb2xsb3dzIHRoZSBjcml0ZXJpb24gd2l0aCBhIHN0YXRpc3RpYyBvZiAxOC4xOTUuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSAzOiBJZiDigJxyZWxhdGlvbnNoaXBz4oCdID4gMywgaXQgZm9sbG93cyBhIGRpZmZlcmVudCBjcml0ZXJpb24gd2l0aCBhIHN0YXRpc3RpYyBvZiAxMy45MzcuPGJyPgoKKipMZWFmIE5vZGVzIChUZXJtaW5hbCBOb2Rlcyk6Kio8YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDQ6IElmIOKAnG1pbGVzdG9uZXPigJ0gPD0gMiBhbmQg4oCcZnVuZGluZ190b3RhbF91c2TigJ0gPD0gMC4yMjg1NDQyLCBpdCBwcmVkaWN0cyBzdGF0dXMgMCB3aXRoIHdlaWdodHMgPSAxMDkuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA1OiBJZiDigJxtaWxlc3RvbmVz4oCdIDw9IDIgYW5kIOKAnGZ1bmRpbmdfdG90YWxfdXNk4oCdID4gMC4yMjg1NDQyLCBpdCBwcmVkaWN0cyBzdGF0dXMgMSB3aXRoIHdlaWdodHMgPSA0Ny48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDY6IElmIOKAnG1pbGVzdG9uZXPigJ0gPiAyLCBpdCBwcmVkaWN0cyBzdGF0dXMgMSB3aXRoIHdlaWdodHMgPSAxNC48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDc6IElmIOKAnGFnZV9sYXN0X21pbGVzdG9uZV95ZWFy4oCdIDw9IDQsIGl0IHByZWRpY3RzIHN0YXR1cyAwIHdpdGggd2VpZ2h0cyA9IDE4Mi48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDg6IElmIOKAnGFnZV9sYXN0X21pbGVzdG9uZV95ZWFy4oCdID4gNCwgaXQgcHJlZGljdHMgc3RhdHVzIDEgd2l0aCB3ZWlnaHRzID0gOTEuPGJyPgoKKipOdW1iZXIgb2YgTm9kZXM6KiogOSAoMSByb290LCA0IGludGVybmFsLCA1IGxlYWYpPGJyPgoKKipNb3N0IEltcG9ydGFudCBGZWF0dXJlczoqKjxicj4KJm5ic3A7Jm5ic3A7MS4gcmVsYXRpb25zaGlwczogQ3JpdGljYWwgYXMgdGhlIHJvb3Qgbm9kZSwgaW5kaWNhdGluZyBoaWdoIGltcG9ydGFuY2UgKDEwMC4wMCUpLjxicj4KJm5ic3A7Jm5ic3A7Mi4gbWlsZXN0b25lczogU2lnbmlmaWNhbnQgZm9yIHNwbGl0cywgZW1waGFzaXppbmcgaXRzIHJlbGV2YW5jZSAoOTYuNjElKS48YnI+CiZuYnNwOyZuYnNwOzMuIGZ1bmRpbmdfdG90YWxfdXNkOiBQbGF5cyBhIHJvbGUgaW4gZGVjaXNpb25zLCBzaG93Y2FzaW5nIGl0cyBpbXBvcnRhbmNlICg3LjY3JSkuPGJyPgoKSW4gc3VtbWFyeSwgdGhpcyBkZWNpc2lvbiB0cmVlLCByb290ZWQgaW4gdGhlIOKAnHJlbGF0aW9uc2hpcHPigJ0gYXR0cmlidXRlLCBlZmZpY2llbnRseSBjbGFzc2lmaWVzIG9ic2VydmF0aW9ucyBiYXNlZCBvbiBrZXkgZmVhdHVyZXMuIFdpdGggNSB0ZXJtaW5hbCBub2RlcywgaXQgcHJvdmlkZXMgZGlzdGluY3QgcHJlZGljdGlvbnMgZm9yIGRpZmZlcmVudCBwYXRocyBpbiB0aGUgZGVjaXNpb24tbWFraW5nIHByb2Nlc3MuIFRoZSBwcmltYXJ5IGF0dHJpYnV0ZXMgaW5mbHVlbmNpbmcgZGVjaXNpb25zIGluY2x1ZGUg4oCccmVsYXRpb25zaGlwcyzigJ0g4oCcbWlsZXN0b25lcyzigJ0gYW5kIOKAnGZ1bmRpbmdfdG90YWxfdXNkLuKAnSBUaGUgdHJlZeKAmXMgc3RydWN0dXJlIGVtcGhhc2l6ZXMgdGhlIHNpZ25pZmljYW5jZSBvZiB0aGVzZSBmZWF0dXJlcyBpbiBkZXRlcm1pbmluZyB0aGUgZmluYWwgc3RhdHVzIHByZWRpY3Rpb24gZm9yIHN0YXJ0dXBzLgoKIyMjIEEuMykgVGVzdGluZwpgYGB7cn0KcHJlZGljdGlvbnNfSUdfODAgPC0gcHJlZGljdChwcmVwcm9jZXNzZWRfZGF0YXNldF9jdHJlZV9JR184MCwgbmV3ZGF0YSA9IHRlc3RfZGF0YV84MCwgdHlwZSA9ICJyZXNwb25zZSIpCmxhYmVsc19JR184MCA8LSB0ZXN0X2RhdGFfODAkc3RhdHVzCmBgYApUaGUgY29kZSBnZW5lcmF0ZXMgcHJlZGljdGlvbnMgZm9yIHRoZSB0ZXN0IGRhdGFzZXQgdXNpbmcgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwuCgojIyMgQS40KSBDb25mdXNpb24gTWF0cml4CmBgYHtyfQpjb25mdXNpb25fbWF0cml4X0lHXzgwIDwtIHRhYmxlKHRlc3RfZGF0YV84MCRzdGF0dXMsIHByZWRpY3Rpb25zX0lHXzgwKQpwcmludChjb25mdXNpb25fbWF0cml4X0lHXzgwKQpgYGAKVGhlIGNvbmZ1c2lvbiBtYXRyaXggZm9yIHRoZSBwcm92aWRlZCBjb2RlIGluZGljYXRlcyB0aGUgZm9sbG93aW5nOjxicj4KCgnigKIJVHJ1ZSBQb3NpdGl2ZSAoVFApOiAzMSBjYXNlcyB3ZXJlIGNvcnJlY3RseSBwcmVkaWN0ZWQgYXMgMC41ODI0Ljxicj4KCeKAoglGYWxzZSBQb3NpdGl2ZSAoRlApOiAxMiBjYXNlcyB3ZXJlIGluY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjQwNDMsIDEgY2FzZSBhcyAwLjU4MjQsIDQgY2FzZXMgYXMgMC4wOTE3NCwgYW5kIDIgY2FzZXMgYXMgMC42NDI5Ljxicj4KCeKAoglUcnVlIE5lZ2F0aXZlIChUTik6IDIzIGNhc2VzIHdlcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjA5MTc0LCA4IGNhc2VzIGFzIDAuNDA0MywgYW5kIDE3IGNhc2VzIGFzIDAuNjQyOS48YnI+CgnigKIJRmFsc2UgTmVnYXRpdmUgKEZOKTogNCBjYXNlcyB3ZXJlIGluY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjA5MTc0IGFuZCA1IGNhc2VzIGFzIDAuNDA0My48YnI+CgkKIyMjIEEuNSkgRXZhbHVhdGlvbiBNZXRyaWNzCmBgYHtyfQptZXRyaWNzX0lHXzgwIDwtIGV2YWx1YXRlX21vZGVsKHByZWRpY3Rpb25zX0lHXzgwLCBsYWJlbHNfSUdfODApCnByaW50KG1ldHJpY3NfSUdfODApCmBgYArigKIgQWNjdXJhY3kgKDI2LjE3JSk6IFRoaXMgbWV0cmljIHJlcHJlc2VudHMgdGhlIG92ZXJhbGwgY29ycmVjdG5lc3Mgb2YgdGhlIG1vZGVs4oCZcyBwcmVkaWN0aW9ucy4gSW4gdGhpcyBjYXNlLCB0aGUgbW9kZWwgYWNoaWV2ZWQgYW4gYWNjdXJhY3kgb2YgMjYuMTclLCBpbmRpY2F0aW5nIGEgcmVsYXRpdmVseSBsb3cgbGV2ZWwgb2YgY29ycmVjdG5lc3MuIDxicj4KCuKAoiBQcmVjaXNpb24gKDg1LjE5JSk6IFByZWNpc2lvbiBtZWFzdXJlcyB0aGUgYWNjdXJhY3kgb2YgcG9zaXRpdmUgcHJlZGljdGlvbnMuIEluIHRoaXMgY29udGV4dCwgdGhlIG1vZGVsIGFjaGlldmVkIGEgcHJlY2lzaW9uIG9mIDg1LjE5JSwgaW5kaWNhdGluZyB0aGF0IHdoZW4gaXQgcHJlZGljdGVkIGEgcG9zaXRpdmUgb3V0Y29tZSwgaXQgd2FzIGNvcnJlY3QgaW4gODUuMTklIG9mIGNhc2VzLjxicj4KCuKAoiBTZW5zaXRpdml0eSAoUmVjYWxsKSAoNzQuMTklKTogU2Vuc2l0aXZpdHksIGFsc28ga25vd24gYXMgcmVjYWxsLCBtZWFzdXJlcyB0aGUgYWJpbGl0eSBvZiB0aGUgbW9kZWwgdG8gY29ycmVjdGx5IGlkZW50aWZ5IHBvc2l0aXZlIGluc3RhbmNlcy4gSW4gdGhpcyBjYXNlLCB0aGUgbW9kZWzigJlzIHNlbnNpdGl2aXR5IGlzIDc0LjE5JSwgc3VnZ2VzdGluZyBhIG1vZGVyYXRlIHBlcmZvcm1hbmNlIGluIGNhcHR1cmluZyBwb3NpdGl2ZSBpbnN0YW5jZXMuPGJyPgoK4oCiIFNwZWNpZmljaXR5ICg1NS41NiUpOiBTcGVjaWZpY2l0eSBtZWFzdXJlcyB0aGUgYWJpbGl0eSBvZiB0aGUgbW9kZWwgdG8gY29ycmVjdGx5IGlkZW50aWZ5IG5lZ2F0aXZlIGluc3RhbmNlcy4gV2l0aCBhIHNwZWNpZmljaXR5IG9mIDU1LjU2JSwgdGhlIG1vZGVsIHBlcmZvcm1lZCBtb2RlcmF0ZWx5IHdlbGwgaW4gYWNjdXJhdGVseSBpZGVudGlmeWluZyBuZWdhdGl2ZSBjYXNlcy48YnI+CgpJbiBzdW1tYXJ5LCB0aGUgbW9kZWwgZXhoaWJpdGVkIGxvdyBhY2N1cmFjeSBidXQgaGlnaCBwcmVjaXNpb24uIFRoZSBzZW5zaXRpdml0eSBpbmRpY2F0ZXMgYSByZWFzb25hYmxlIGFiaWxpdHkgdG8gY2FwdHVyZSBwb3NpdGl2ZSBpbnN0YW5jZXMsIHdoaWxlIHRoZSBzcGVjaWZpY2l0eSBzdWdnZXN0cyByb29tIGZvciBpbXByb3ZlbWVudCBpbiBpZGVudGlmeWluZyBuZWdhdGl2ZSBjYXNlcy48YnI+CgoKIyMjIEIpIEM1MCBBbGdvcml0aG06IEdhaW4gUmF0aW8KCiMjIyBCLjEpIENvbnN0cnVjdGluZyB0aGUgRGVjaXNpb24gVHJlZQoKYGBge3J9CmxpYnJhcnkoQzUwKQp0cmFpbl9kYXRhXzgwJHN0YXR1cyA8LSBhcy5mYWN0b3IodHJhaW5fZGF0YV84MCRzdGF0dXMpCgpDNUZpdF84MCA8LSBDNS4wKHN0YXR1cyB+IC4sIGRhdGEgPSB0cmFpbl9kYXRhXzgwLCBjb250cm9sID0gQzUuMENvbnRyb2woZWFybHlTdG9wcGluZyA9IEZBTFNFLCBDRiA9IDAuMjUpKQpzdW1tYXJ5KEM1Rml0XzgwKQpgYGAKVGhlIGNvZGUgYnVpbGRzIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsLiAKCgojIyMgQi4yKSBQbG90dGluZwpgYGB7cn0KcGxvdChDNUZpdF84MCwgdHlwZSA9ICJzaW1wbGUiKQpgYGAKKipSb290IE5vZGUgKE5vZGUgMSk6Kio8YnI+CiZuYnNwOyZuYnNwO+KAoiBTcGxpdHRpbmcgYXR0cmlidXRlOiDigJxyZWxhdGlvbnNoaXBz4oCdIDw9IDMsIGNvbnNpZGVyaW5nIDQ0MyBvYnNlcnZhdGlvbnMgd2l0aCBhIGNyaXRlcmlvbiBvZiAxIGFuZCBhIHN0YXRpc3RpYyBvZiA3MC45MDUgKGV4cGVjdGVkIGxvc3M6IDcwLjkwNSUpLjxicj4KCioqQnJhbmNoZXM6Kio8YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDI6IElmIOKAnHJlbGF0aW9uc2hpcHPigJ0gPD0gMywgaXQgZXZhbHVhdGVzIHRoZSBuZXh0IGNyaXRlcmlvbiB3aXRoIGEgc3RhdGlzdGljIG9mIDE4LjE5NS48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDM6IElmIOKAnHJlbGF0aW9uc2hpcHPigJ0gPiAzLCBpdCBldmFsdWF0ZXMgYSBkaWZmZXJlbnQgY3JpdGVyaW9uIHdpdGggYSBzdGF0aXN0aWMgb2YgMTMuOTM3Ljxicj4KCioqTGVhZiBOb2RlcyAoVGVybWluYWwgTm9kZXMpOioqPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA0OiBUZXJtaW5hbCBub2RlIHdoZXJlIHRoZSBkZWNpc2lvbiB0cmVlIHByZWRpY3RzIGJhc2VkIG9uIHdlaWdodHMgPSAxMDkuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA1OiBUZXJtaW5hbCBub2RlIHdoZXJlIHRoZSBkZWNpc2lvbiB0cmVlIHByZWRpY3RzIGJhc2VkIG9uIHdlaWdodHMgPSA0Ny48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDY6IFRlcm1pbmFsIG5vZGUgd2hlcmUgdGhlIGRlY2lzaW9uIHRyZWUgcHJlZGljdHMgYmFzZWQgb24gd2VpZ2h0cyA9IDE0Ljxicj4KJm5ic3A7Jm5ic3A74oCiIE5vZGUgNzogVGVybWluYWwgbm9kZSB3aGVyZSB0aGUgZGVjaXNpb24gdHJlZSBwcmVkaWN0cyBiYXNlZCBvbiB3ZWlnaHRzID0gMTgyLjxicj4KJm5ic3A7Jm5ic3A74oCiIE5vZGUgODogVGVybWluYWwgbm9kZSB3aGVyZSB0aGUgZGVjaXNpb24gdHJlZSBwcmVkaWN0cyBiYXNlZCBvbiB3ZWlnaHRzID0gOTEuPGJyPgoKKipOdW1iZXIgb2YgTm9kZXM6KiogOSAoMSByb290LCA0IGludGVybmFsLCA1IGxlYWYpPGJyPgoKKipNb3N0IEltcG9ydGFudCBGZWF0dXJlczoqKjxicj4KJm5ic3A7Jm5ic3A7MS4gcmVsYXRpb25zaGlwczogQ3JpdGljYWwgYXMgdGhlIHJvb3Qgbm9kZSwgaW5kaWNhdGluZyBoaWdoIGltcG9ydGFuY2UgKDEwMC4wMCUpLjxicj4KJm5ic3A7Jm5ic3A7Mi4gbWlsZXN0b25lczogU2lnbmlmaWNhbnQgZm9yIGZ1cnRoZXIgc3BsaXRzLCBzaG93Y2FzaW5nIGl0cyByZWxldmFuY2UgKDk2LjYxJSkuPGJyPgombmJzcDsmbmJzcDszLiBmb3VuZGVkX2F0OiBQbGF5cyBhIGtleSByb2xlIGluIGRlY2lzaW9uLW1ha2luZyAoNTUuNzYlKS48YnI+CgpJbiBzdW1tYXJ5LCB0aGlzIGRlY2lzaW9uIHRyZWUsIHJvb3RlZCBpbiB0aGUg4oCccmVsYXRpb25zaGlwc+KAnSBhdHRyaWJ1dGUsIGVmZmljaWVudGx5IGNsYXNzaWZpZXMgb2JzZXJ2YXRpb25zIGJhc2VkIG9uIGtleSBmZWF0dXJlcy4gV2l0aCA1IHRlcm1pbmFsIG5vZGVzLCBpdCBwcm92aWRlcyBkaXN0aW5jdCBwcmVkaWN0aW9ucyBmb3IgZGlmZmVyZW50IHBhdGhzIGluIHRoZSBkZWNpc2lvbi1tYWtpbmcgcHJvY2Vzcy4gVGhlIHByaW1hcnkgYXR0cmlidXRlcyBpbmZsdWVuY2luZyBkZWNpc2lvbnMgaW5jbHVkZSDigJxyZWxhdGlvbnNoaXBzLOKAnSDigJxtaWxlc3RvbmVzLOKAnSBhbmQg4oCcZm91bmRlZF9hdC7igJ0gVGhlIHRyZWXigJlzIHN0cnVjdHVyZSBlbXBoYXNpemVzIHRoZSBzaWduaWZpY2FuY2Ugb2YgdGhlc2UgZmVhdHVyZXMgaW4gZGV0ZXJtaW5pbmcgdGhlIGZpbmFsIHN0YXR1cyBwcmVkaWN0aW9uIGZvciBzdGFydHVwcy4KCiMjIyBCLjMpIFRlc3RpbmcKYGBge3J9CnByZWRpY3Rpb25zX0dSXzgwIDwtIHByZWRpY3QoQzVGaXRfODAsIG5ld2RhdGEgPSB0ZXN0X2RhdGFfODApCmxhYmVsc19HUl84MCA8LSB0ZXN0X2RhdGFfODAkc3RhdHVzCmBgYApUaGUgY29kZSBnZW5lcmF0ZXMgcHJlZGljdGlvbnMgZm9yIHRoZSB0ZXN0IGRhdGFzZXQgdXNpbmcgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwuCgoKIyMjIEIuNCkgQ29uZnVzaW9uIE1hdHJpeApgYGB7cn0KY29uZnVzaW9uX21hdHJpeF9HUl84MCA8LSB0YWJsZSh0ZXN0X2RhdGFfODAkc3RhdHVzLCBwcmVkaWN0aW9uc19HUl84MCkKcHJpbnQoY29uZnVzaW9uX21hdHJpeF9HUl84MCkKYGBgClRoZSBjb25mdXNpb24gbWF0cml4IGZvciB0aGUgcHJvdmlkZWQgY29kZSBpbmRpY2F0ZXMgdGhlIGZvbGxvd2luZzo8YnI+CgoJ4oCiCVRydWUgUG9zaXRpdmUgKFRQKTogNDYgY2FzZXMgd2VyZSBjb3JyZWN0bHkgcHJlZGljdGVkIGFzIDEuPGJyPgoJ4oCiCUZhbHNlIFBvc2l0aXZlIChGUCk6IDEyIGNhc2VzIHdlcmUgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIDEsIGFuZCAxMyBjYXNlcyB3ZXJlIGluY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjxicj4KCeKAoglUcnVlIE5lZ2F0aXZlIChUTik6IDM2IGNhc2VzIHdlcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjxicj4KCeKAoglGYWxzZSBOZWdhdGl2ZSAoRk4pOiAwIGNhc2VzIHdlcmUgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIDAuPGJyPgoKIyMjIEIuNSkgRXZhbHVhdGlvbiBNZXRyaWNzCmBgYHtyfQptZXRyaWNzX0dSXzgwIDwtIGV2YWx1YXRlX21vZGVsKHByZWRpY3Rpb25zX0dSXzgwLCBsYWJlbHNfR1JfODApCmBgYArigKIgQWNjdXJhY3kgKDc2LjY0JSk6IFRoaXMgbWV0cmljIHJlcHJlc2VudHMgdGhlIG92ZXJhbGwgY29ycmVjdG5lc3Mgb2YgdGhlIG1vZGVs4oCZcyBwcmVkaWN0aW9ucy4gSW4gdGhpcyBjYXNlLCB0aGUgbW9kZWwgYWNoaWV2ZWQgYW4gYWNjdXJhY3kgb2YgNzYuNjQlLCBpbmRpY2F0aW5nIGEgcmVsYXRpdmVseSBoaWdoIGxldmVsIG9mIGNvcnJlY3RuZXNzLjxicj4KCuKAoiBQcmVjaXNpb24gKDczLjQ3JSk6IFByZWNpc2lvbiBtZWFzdXJlcyB0aGUgYWNjdXJhY3kgb2YgcG9zaXRpdmUgcHJlZGljdGlvbnMuIEluIHRoaXMgY29udGV4dCwgdGhlIG1vZGVsIGFjaGlldmVkIGEgcHJlY2lzaW9uIG9mIDczLjQ3JSwgaW5kaWNhdGluZyB0aGF0IHdoZW4gaXQgcHJlZGljdGVkIGEgcG9zaXRpdmUgb3V0Y29tZSwgaXQgd2FzIGNvcnJlY3QgaW4gNzMuNDclIG9mIGNhc2VzLjxicj4KCuKAoiBTZW5zaXRpdml0eSAoUmVjYWxsKSAoNzUlKTogU2Vuc2l0aXZpdHksIGFsc28ga25vd24gYXMgcmVjYWxsLCBtZWFzdXJlcyB0aGUgYWJpbGl0eSBvZiB0aGUgbW9kZWwgdG8gY29ycmVjdGx5IGlkZW50aWZ5IHBvc2l0aXZlIGluc3RhbmNlcy4gSW4gdGhpcyBjYXNlLCB0aGUgbW9kZWzigJlzIHNlbnNpdGl2aXR5IGlzIDc1JSwgc3VnZ2VzdGluZyBhIGdvb2QgcGVyZm9ybWFuY2UgaW4gY2FwdHVyaW5nIHBvc2l0aXZlIGluc3RhbmNlcy48YnI+CgrigKIgU3BlY2lmaWNpdHkgKDc3Ljk3JSk6IFNwZWNpZmljaXR5IG1lYXN1cmVzIHRoZSBhYmlsaXR5IG9mIHRoZSBtb2RlbCB0byBjb3JyZWN0bHkgaWRlbnRpZnkgbmVnYXRpdmUgaW5zdGFuY2VzLiBXaXRoIGEgc3BlY2lmaWNpdHkgb2YgNzcuOTclLCB0aGUgbW9kZWwgcGVyZm9ybWVkIHdlbGwgaW4gYWNjdXJhdGVseSBpZGVudGlmeWluZyBuZWdhdGl2ZSBjYXNlcy48YnI+CgpJbiBzdW1tYXJ5LCB0aGUgbW9kZWwgZXhoaWJpdGVkIGhpZ2ggYWNjdXJhY3ksIHByZWNpc2lvbiwgYW5kIHNlbnNpdGl2aXR5LiBUaGUgc3BlY2lmaWNpdHkgaW5kaWNhdGVzIGEgZ29vZCBhYmlsaXR5IHRvIGlkZW50aWZ5IG5lZ2F0aXZlIGluc3RhbmNlcy4KCiMjIyBDQVJUIEFsZ29yaXRobTogR2luaSBJbmRleAoKIyMjIEMuMSkgQ29uc3RydWN0aW5nIHRoZSBEZWNpc2lvbiBUcmVlCmBgYHtyfQpsaWJyYXJ5KHJwYXJ0KQpwcmVwcm9jZXNzZWRfZGF0YXNldF9jdHJlZV9HSV84MCA8LSBycGFydChzdGF0dXMgfiAuLCBkYXRhID0gdHJhaW5fZGF0YV84MCwgbWV0aG9kID0gImNsYXNzIiwgcGFybXMgPSBsaXN0KHNwbGl0ID0gImdpbmkiKSkKYGBgClRoZSBjb2RlIGJ1aWxkcyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbC4gCgojIyMgQy4yKSBQbG90dGluZwpgYGB7cn0KcHJpbnQocHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfR0lfODApCnJwYXJ0LnBsb3QocHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfR0lfODApCmBgYAoKKipSb290IE5vZGUgKE5vZGUgMSk6Kio8YnI+CiZuYnNwOyZuYnNwO+KAoiBTcGxpdHRpbmcgYXR0cmlidXRlOiDigJxyZWxhdGlvbnNoaXBz4oCdIDwgMy41LCBjb25zaWRlcmluZyA0NDMgb2JzZXJ2YXRpb25zIHdpdGggYW4gZXhwZWN0ZWQgbG9zcyBvZiAwLjUxMjQxNTMgKDUxLjIlIGNsYXNzIDAsIDQ4LjglIGNsYXNzIDEpLjxicj4KCioqQnJhbmNoZXM6Kio8YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDI6IElmIOKAnHJlbGF0aW9uc2hpcHPigJ0gPCAzLjUsIGl0IHByZWRpY3RzIGNsYXNzIDAgd2l0aCBhbiBleHBlY3RlZCBsb3NzIG9mIDAuNzc2NDcwNiAoNzcuNiUgY2xhc3MgMCwgMjIuNCUgY2xhc3MgMSkuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA0OiBJZiDigJxhZ2VfbGFzdF9taWxlc3RvbmVfeWVhcuKAnSA8IDUuNSAodW5kZXIgTm9kZSAyKSwgaXQgcHJlZGljdHMgY2xhc3MgMCB3aXRoIGFuIGV4cGVjdGVkIGxvc3Mgb2YgMC44NTQxNjY3ICg4NS40JSBjbGFzcyAwLCAxNC42JSBjbGFzcyAxKS4gKjxicj4KJm5ic3A7Jm5ic3A74oCiIE5vZGUgNTogSWYg4oCcYWdlX2xhc3RfbWlsZXN0b25lX3llYXLigJ0gPj0gNS41ICh1bmRlciBOb2RlIDIpLCBpdCBwcmVkaWN0cyBjbGFzcyAxIHdpdGggYW4gZXhwZWN0ZWQgbG9zcyBvZiAwLjMzMzMzMzMgKDMzLjMlIGNsYXNzIDAsIDY2LjclIGNsYXNzIDEpLiAqPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSAzOiBJZiDigJxyZWxhdGlvbnNoaXBz4oCdID49IDMuNSwgaXQgcHJlZGljdHMgY2xhc3MgMSB3aXRoIGFuIGV4cGVjdGVkIGxvc3Mgb2YgMC4zNDc5ODUzICgzNC44JSBjbGFzcyAwLCA2NS4yJSBjbGFzcyAxKS48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDY6IElmIOKAnGFnZV9sYXN0X2Z1bmRpbmdfeWVhcuKAnSA8IDAuNSAodW5kZXIgTm9kZSAzKSwgaXQgcHJlZGljdHMgY2xhc3MgMCB3aXRoIGFuIGV4cGVjdGVkIGxvc3Mgb2YgMC42MDg2OTU3ICg2MC45JSBjbGFzcyAwLCAzOS4xJSBjbGFzcyAxKS48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDEyOiBJZiDigJxtaWxlc3RvbmVz4oCdIDwgMi41ICh1bmRlciBOb2RlIDYpLCBpdCBwcmVkaWN0cyBjbGFzcyAwIHdpdGggYW4gZXhwZWN0ZWQgbG9zcyBvZiAwLjg1NzE0MjkgKDg1LjclIGNsYXNzIDAsIDE0LjMlIGNsYXNzIDEpLiAqPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSAxMzogSWYg4oCcbWlsZXN0b25lc+KAnSA+PSAyLjUgKHVuZGVyIE5vZGUgNiksIGl0IHByZWRpY3RzIGNsYXNzIDEgd2l0aCBhbiBleHBlY3RlZCBsb3NzIG9mIDAuMjIyMjIyMiAoMjIuMiUgY2xhc3MgMCwgNzcuOCUgY2xhc3MgMSkuICo8YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDc6IElmIOKAnGFnZV9sYXN0X2Z1bmRpbmdfeWVhcuKAnSA+PSAwLjUgKHVuZGVyIE5vZGUgMyksIGl0IHByZWRpY3RzIGNsYXNzIDEgd2l0aCBhbiBleHBlY3RlZCBsb3NzIG9mIDAuMjk1MTU0MiAoMjkuNSUgY2xhc3MgMCwgNzAuNSUgY2xhc3MgMSkuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSAxNDogSWYg4oCcZnVuZGluZ190b3RhbF91c2TigJ0gPCAwLjExNjgwMzUgKHVuZGVyIE5vZGUgNyksIGl0IHByZWRpY3RzIGNsYXNzIDEgd2l0aCBhbiBleHBlY3RlZCBsb3NzIG9mIDAuNDA0NzYxOSAoNDAuNSUgY2xhc3MgMCwgNTkuNSUgY2xhc3MgMSkuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSAyODogVGVybWluYWwgbm9kZSBwcmVkaWN0aW5nIGNsYXNzIDAgKDEwMCUgY2xhc3MgMCwgMCUgY2xhc3MgMSkuICo8YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDI5OiBUZXJtaW5hbCBub2RlIHByZWRpY3RpbmcgY2xhc3MgMSB3aXRoIGFuIGV4cGVjdGVkIGxvc3Mgb2YgMC40Mjg1NzE0ICg0Mi45JSBjbGFzcyAwLCA1Ny4xJSBjbGFzcyAxKS4gKjxicj4KCioqTnVtYmVyIG9mIE5vZGVzOioqIDggKDEgcm9vdCwgNiBpbnRlcm5hbCwgMiBsZWFmKTxicj4KCioqTW9zdCBJbXBvcnRhbnQgRmVhdHVyZXM6Kio8YnI+CiZuYnNwOyZuYnNwOzEuIHJlbGF0aW9uc2hpcHM6IENyaXRpY2FsIGFzIHRoZSByb290IG5vZGUsIGluZGljYXRpbmcgaGlnaCBpbXBvcnRhbmNlLjxicj4KJm5ic3A7Jm5ic3A7Mi4gYWdlX2xhc3RfbWlsZXN0b25lX3llYXI6IFNpZ25pZmljYW50IGZvciBmdXJ0aGVyIHNwbGl0cywgc2hvd2Nhc2luZyBpdHMgcmVsZXZhbmNlLjxicj4KJm5ic3A7Jm5ic3A7My4gYWdlX2xhc3RfZnVuZGluZ195ZWFyOiBVc2VkIGZvciBhZGRpdGlvbmFsIHNwbGl0cywgZW1waGFzaXppbmcgaXRzIGNvbnRyaWJ1dGlvbi48YnI+CgpJbiBzdW1tYXJ5LCB0aGlzIHRyZWUgdXRpbGl6ZXMgdGhlIOKAnHJlbGF0aW9uc2hpcHPigJ0gZmVhdHVyZSBmb3IgdGhlIGluaXRpYWwgc3BsaXQsIGZvbGxvd2VkIGJ5IGFkZGl0aW9uYWwgY3JpdGVyaWEgYXQgZWFjaCBicmFuY2hpbmcgcG9pbnQsIHVsdGltYXRlbHkgbGVhZGluZyB0byBwcmVkaWN0aW9ucyBhdCB0aGUgdGVybWluYWwgbm9kZXMuIFRoZSB3ZWlnaHRzIGFzc29jaWF0ZWQgd2l0aCBlYWNoIHRlcm1pbmFsIG5vZGUgaW5kaWNhdGUgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgZmFsbGluZyBpbnRvIGVhY2ggY2F0ZWdvcnkuIEZvciB0aGlzIHRyZWUsIG49IDQ0My4KCiMjIyBDLjMpIFRlc3RpbmcKYGBge3J9CnByZWRpY3Rpb25zX0dJXzgwIDwtIHByZWRpY3QocHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfR0lfODAsIG5ld2RhdGEgPSB0ZXN0X2RhdGFfODAsIHR5cGUgPSAiY2xhc3MiKQpsYWJlbHNfR0lfODAgPC0gdGVzdF9kYXRhXzgwJHN0YXR1cwpgYGAKVGhlIGNvZGUgZ2VuZXJhdGVzIHByZWRpY3Rpb25zIGZvciB0aGUgdGVzdCBkYXRhc2V0IHVzaW5nIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsLgoKIyMjIEMuNCkgQ29uZnVzaW9uIE1hdHJpeApgYGB7cn0KY29uZnVzaW9uX21hdHJpeF9HSV84MCA8LSB0YWJsZSh0ZXN0X2RhdGFfODAkc3RhdHVzLCBwcmVkaWN0aW9uc19HSV84MCkKcHJpbnQoY29uZnVzaW9uX21hdHJpeF9HSV84MCkKYGBgClRoZSBjb25mdXNpb24gbWF0cml4IGZvciB0aGUgcHJvdmlkZWQgY29kZSBpbmRpY2F0ZXMgdGhlIGZvbGxvd2luZzo8YnI+CgoJ4oCiCVRydWUgUG9zaXRpdmUgKFRQKTogNDYgY2FzZXMgd2VyZSBjb3JyZWN0bHkgcHJlZGljdGVkIGFzIDEuPGJyPgoJ4oCiCUZhbHNlIFBvc2l0aXZlIChGUCk6IDE1IGNhc2VzIHdlcmUgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIDEsIGFuZCAxMyBjYXNlcyB3ZXJlIGluY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjxicj4KCeKAoglUcnVlIE5lZ2F0aXZlIChUTik6IDMzIGNhc2VzIHdlcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjxicj4KCeKAoglGYWxzZSBOZWdhdGl2ZSAoRk4pOiAwIGNhc2VzIHdlcmUgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIDAuPGJyPgoJCiMjIyBDLjUpIEV2YWx1YXRpb24gTWV0cmljcwpgYGB7cn0KbWV0cmljc19HSV84MCA8LSBldmFsdWF0ZV9tb2RlbChwcmVkaWN0aW9uc19HSV84MCwgbGFiZWxzX0dJXzgwKQpgYGAK4oCiIEFjY3VyYWN5ICg3My44MyUpOiBUaGlzIG1ldHJpYyByZXByZXNlbnRzIHRoZSBvdmVyYWxsIGNvcnJlY3RuZXNzIG9mIHRoZSBtb2RlbOKAmXMgcHJlZGljdGlvbnMuIEluIHRoaXMgY2FzZSwgdGhlIG1vZGVsIGFjaGlldmVkIGFuIGFjY3VyYWN5IG9mIDczLjgzJSwgaW5kaWNhdGluZyBhIHJlbGF0aXZlbHkgaGlnaCBsZXZlbCBvZiBjb3JyZWN0bmVzcy48YnI+CgrigKIgUHJlY2lzaW9uICg3MS43NCUpOiBQcmVjaXNpb24gbWVhc3VyZXMgdGhlIGFjY3VyYWN5IG9mIHBvc2l0aXZlIHByZWRpY3Rpb25zLiBJbiB0aGlzIGNvbnRleHQsIHRoZSBtb2RlbCBhY2hpZXZlZCBhIHByZWNpc2lvbiBvZiA3MS43NCUsIGluZGljYXRpbmcgdGhhdCB3aGVuIGl0IHByZWRpY3RlZCBhIHBvc2l0aXZlIG91dGNvbWUsIGl0IHdhcyBjb3JyZWN0IGluIDcxLjc0JSBvZiBjYXNlcy48YnI+CgrigKIgU2Vuc2l0aXZpdHkgKFJlY2FsbCkgKDY4Ljc1JSk6IFNlbnNpdGl2aXR5LCBhbHNvIGtub3duIGFzIHJlY2FsbCwgbWVhc3VyZXMgdGhlIGFiaWxpdHkgb2YgdGhlIG1vZGVsIHRvIGNvcnJlY3RseSBpZGVudGlmeSBwb3NpdGl2ZSBpbnN0YW5jZXMuIEluIHRoaXMgY2FzZSwgdGhlIG1vZGVs4oCZcyBzZW5zaXRpdml0eSBpcyA2OC43NSUsIHN1Z2dlc3RpbmcgYSBtb2RlcmF0ZSBwZXJmb3JtYW5jZSBpbiBjYXB0dXJpbmcgcG9zaXRpdmUgaW5zdGFuY2VzLjxicj4KCuKAoiBTcGVjaWZpY2l0eSAoNzcuOTclKTogU3BlY2lmaWNpdHkgbWVhc3VyZXMgdGhlIGFiaWxpdHkgb2YgdGhlIG1vZGVsIHRvIGNvcnJlY3RseSBpZGVudGlmeSBuZWdhdGl2ZSBpbnN0YW5jZXMuIFdpdGggYSBzcGVjaWZpY2l0eSBvZiA3Ny45NyUsIHRoZSBtb2RlbCBwZXJmb3JtZWQgd2VsbCBpbiBhY2N1cmF0ZWx5IGlkZW50aWZ5aW5nIG5lZ2F0aXZlIGNhc2VzLjxicj4KCkluIHN1bW1hcnksIHRoZSBtb2RlbCBleGhpYml0ZWQgaGlnaCBhY2N1cmFjeSwgYW5kIHNwZWNpZmljaXR5LiBUaGUgcHJlY2lzaW9uIGFuZCBzZW5zaXRpdml0eSBzdWdnZXN0IGEgcmVhc29uYWJsZSBhYmlsaXR5IHRvIG1ha2UgY29ycmVjdCBwcmVkaWN0aW9ucywgZXNwZWNpYWxseSBpbiBjYXB0dXJpbmcgcG9zaXRpdmUgaW5zdGFuY2VzLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIFRoaXJkIFBhcnRpdGlvbjogKDkwJSB0cmFpbmluZywgMTAlIHRlc3RpbmcpCgojIyMjIFBhcnRpdGlvbmluZwpgYGB7cn0Kc2V0LnNlZWQoMTIzNCkKaW5kIDwtIHNhbXBsZSgyLCBucm93KGJhbGFuY2VkX3ByZXByb2Nlc3NlZF9kYXRhc2V0KSwgcmVwbGFjZT1UUlVFLCBwcm9iPWMoMC45MCwgMC4xMCkpCnRyYWluX2RhdGFfOTAgPC0gYmFsYW5jZWRfcHJlcHJvY2Vzc2VkX2RhdGFzZXRbaW5kID09IDEsXQp0ZXN0X2RhdGFfOTAgPC0gYmFsYW5jZWRfcHJlcHJvY2Vzc2VkX2RhdGFzZXRbaW5kID09IDIsXQpgYGAKVGhlIGNvZGUgc3BsaXRzIHRoZSBwcmVwcm9jZXNzZWQgZGF0YXNldCBpbnRvIDkwJSB0cmFpbmluZyBkYXRhIGFuZCAxMCUgdGVzdGluZyBkYXRhLiAKCiMjIyMgVmVyaWZ5IFBhcnRpdGlvbmluZwpgYGB7cn0KZGltKHRyYWluX2RhdGFfOTApCmRpbSh0ZXN0X2RhdGFfOTApCmBgYApUaGUgdHJhaW5pbmcgZGF0YSBjb25zaXN0IG9mIDQ4NyByb3dzLiAKVGhlIHRlc3RpbmcgZGF0YSBjb25zaXN0IG9mIDYzIHJvd3MuCgojIyMgQS4xKSBDb25zdHJ1Y3RpbmcgdGhlIERlY2lzaW9uIFRyZWUKYGBge3J9CmxpYnJhcnkocGFydHkpCm15Rm9ybXVsYSA8LSBzdGF0dXMgfiBzdGF0ZV9jb2RlICsgY2l0eSArIG5hbWUgKyBmb3VuZGVkX2F0ICsgY2xvc2VkX2F0ICsgZmlyc3RfZnVuZGluZ19hdCArIGxhc3RfZnVuZGluZ19hdCArIAogICAgICAgICAgICBhZ2VfZmlyc3RfZnVuZGluZ195ZWFyICsgYWdlX2xhc3RfZnVuZGluZ195ZWFyICsgYWdlX2ZpcnN0X21pbGVzdG9uZV95ZWFyICsgYWdlX2xhc3RfbWlsZXN0b25lX3llYXIgKyAKICAgICAgICAgICAgcmVsYXRpb25zaGlwcyArIGZ1bmRpbmdfcm91bmRzICsgZnVuZGluZ190b3RhbF91c2QgKyBtaWxlc3RvbmVzICsgaXNfQ0EgKyBpc19OWSArIGlzX01BICsgaXNfVFggKyBpc19vdGhlcnN0YXRlICsgCiAgICAgICAgICAgIGNhdGVnb3J5X2NvZGUgKyBpc19zb2Z0d2FyZSArIGlzX3dlYiArIGlzX21vYmlsZSArIGlzX2VudGVycHJpc2UgKyBpc19hZHZlcnRpc2luZyArIGlzX2dhbWVzdmlkZW8gKyBpc19lY29tbWVyY2UgKyAKICAgICAgICAgICAgaXNfYmlvdGVjaCArIGlzX2NvbnN1bHRpbmcgKyBpc19vdGhlcmNhdGVnb3J5ICsgaGFzX1ZDICsgaGFzX2FuZ2VsICsgaGFzX3JvdW5kQSArIGhhc19yb3VuZEIgKyBoYXNfcm91bmRDICsgCiAgICAgICAgICAgIGhhc19yb3VuZEQgKyBhdmdfcGFydGljaXBhbnRzICsgaXNfdG9wNTAwCnByZXByb2Nlc3NlZF9kYXRhc2V0X2N0cmVlX0lHXzkwIDwtIGN0cmVlKG15Rm9ybXVsYSwgZGF0YSA9IHRyYWluX2RhdGFfOTApCgp0YWJsZShwcmVkaWN0KHByZXByb2Nlc3NlZF9kYXRhc2V0X2N0cmVlX0lHXzkwKSwgdHJhaW5fZGF0YV85MCRzdGF0dXMpCmBgYApUaGUgY29kZSBidWlsZHMgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwuIAoKIyMjIEEuMikgUGxvdHRpbmcKYGBge3J9CnByaW50KHByZXByb2Nlc3NlZF9kYXRhc2V0X2N0cmVlX0lHXzkwKQpwbG90KHByZXByb2Nlc3NlZF9kYXRhc2V0X2N0cmVlX0lHXzkwKQpwbG90KHByZXByb2Nlc3NlZF9kYXRhc2V0X2N0cmVlX0lHXzkwLCB0eXBlID0gInNpbXBsZSIpCmBgYAoqKlJvb3QgTm9kZSAoTm9kZSAxKToqKjxicj4KJm5ic3A7Jm5ic3A74oCiIFNwbGl0dGluZyBhdHRyaWJ1dGU6IOKAnHJlbGF0aW9uc2hpcHPigJ0gPD0gMzsgY3JpdGVyaW9uID0gMSwgc3RhdGlzdGljID0gODUuNTEzLCBjb25zaWRlcmluZyA0ODcgb2JzZXJ2YXRpb25zLjxicj4KCioqQnJhbmNoZXM6Kio8YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDI6IElmIOKAnHJlbGF0aW9uc2hpcHPigJ0gPD0gMywgaXQgcHJvY2VlZHMgdG8gdGhlIG5leHQgc3BsaXQgYmFzZWQgb24g4oCcbWlsZXN0b25lc+KAnSA8PSAyOyBjcml0ZXJpb24gPSAwLjk5OSwgc3RhdGlzdGljID0gMTguMzYyLjxicj4KJm5ic3A7Jm5ic3A74oCiIE5vZGUgMzogSWYg4oCcbWlsZXN0b25lc+KAnSA8PSAyIGFuZCDigJxpc190b3A1MDDigJ0gPD0gMCwgaXQgcHJlZGljdHMgY2xhc3MgMC4gKjxicj4KJm5ic3A7Jm5ic3A74oCiIE5vZGUgNTogSWYg4oCcbWlsZXN0b25lc+KAnSA8PSAyIGFuZCDigJxpc190b3A1MDDigJ0gPiAwLCBpdCBwcmVkaWN0cyBjbGFzcyAxLiAqPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA2OiBJZiDigJxtaWxlc3RvbmVz4oCdID4gMiwgaXQgcHJlZGljdHMgY2xhc3MgMS4gKjxicj4KJm5ic3A7Jm5ic3A74oCiIE5vZGUgNzogSWYg4oCccmVsYXRpb25zaGlwc+KAnSA+IDMgYW5kIOKAnGFnZV9sYXN0X21pbGVzdG9uZV95ZWFy4oCdIDw9IDAsIGl0IHByZWRpY3RzIGNsYXNzIDAuICo8YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDg6IElmIOKAnHJlbGF0aW9uc2hpcHPigJ0gPiAzIGFuZCDigJxhZ2VfbGFzdF9taWxlc3RvbmVfeWVhcuKAnSA+IDAgYW5kIOKAnGlzX3RvcDUwMOKAnSA8PSAwLCBpdCBwcmVkaWN0cyBjbGFzcyAwLiAqPGJyPgoKKipMZWFmIE5vZGVzIChUZXJtaW5hbCBOb2Rlcyk6Kio8YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDQ6IFRlcm1pbmFsIG5vZGUgcHJlZGljdGluZyBjbGFzcyAwLCB3aXRoIHdlaWdodHMgPSA3MS48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDU6IFRlcm1pbmFsIG5vZGUgcHJlZGljdGluZyBjbGFzcyAxLCB3aXRoIHdlaWdodHMgPSAxMDIuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA2OiBUZXJtaW5hbCBub2RlIHByZWRpY3RpbmcgY2xhc3MgMSwgd2l0aCB3ZWlnaHRzID0gMTQuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA4OiBUZXJtaW5hbCBub2RlIHByZWRpY3RpbmcgY2xhc3MgMCwgd2l0aCB3ZWlnaHRzID0gMTEuPGJyPgoKKipOdW1iZXIgb2YgTm9kZXM6KiogOSAoMSByb290LCA1IGludGVybmFsLCA0IGxlYWYpPGJyPgoKKipNb3N0IEltcG9ydGFudCBGZWF0dXJlczoqKjxicj4KJm5ic3A7Jm5ic3A7MS4gcmVsYXRpb25zaGlwczogQ3JpdGljYWwgYXMgdGhlIHJvb3Qgbm9kZSwgaW5kaWNhdGluZyBoaWdoIGltcG9ydGFuY2UuPGJyPgombmJzcDsmbmJzcDsyLiBtaWxlc3RvbmVzOiBTaWduaWZpY2FudCBmb3IgZnVydGhlciBzcGxpdHMsIHNob3djYXNpbmcgaXRzIHJlbGV2YW5jZS48YnI+CiZuYnNwOyZuYnNwOzMuIGlzX3RvcDUwMDogVXNlZCBmb3IgYWRkaXRpb25hbCBzcGxpdHMsIGVtcGhhc2l6aW5nIGl0cyBjb250cmlidXRpb24uPGJyPgoKSW4gc3VtbWFyeSwgdGhpcyBjb25kaXRpb25hbCBpbmZlcmVuY2UgdHJlZSB1dGlsaXplcyDigJxyZWxhdGlvbnNoaXBz4oCdIGFzIHRoZSBpbml0aWFsIHNwbGl0LCBmb2xsb3dlZCBieSBjcml0ZXJpYSBiYXNlZCBvbiDigJxtaWxlc3RvbmVz4oCdIGFuZCDigJxpc190b3A1MDDigJ0gYXQgZGlmZmVyZW50IGJyYW5jaGluZyBwb2ludHMuIFRoZSB0ZXJtaW5hbCBub2RlcyBwcm92aWRlIHByZWRpY3Rpb25zIHdpdGggYXNzb2NpYXRlZCB3ZWlnaHRzIHJlZmxlY3RpbmcgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMuIEZvciB0aGlzIHRyZWUsIG49IDQ4Ny4KCiMjIyBBLjMpIFRlc3RpbmcKYGBge3J9CnByZWRpY3Rpb25zX0lHXzkwIDwtIHByZWRpY3QocHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfSUdfOTAsIG5ld2RhdGEgPSB0ZXN0X2RhdGFfOTAsIHR5cGUgPSAicmVzcG9uc2UiKQpsYWJlbHNfSUdfOTAgPC0gdGVzdF9kYXRhXzkwJHN0YXR1cwpgYGAKVGhlIGNvZGUgZ2VuZXJhdGVzIHByZWRpY3Rpb25zIGZvciB0aGUgdGVzdCBkYXRhc2V0IHVzaW5nIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsLgoKCiMjIyBBLjQpIENvbmZ1c2lvbiBNYXRyaXgKYGBge3J9CmNvbmZ1c2lvbl9tYXRyaXhfSUdfOTAgPC0gdGFibGUodGVzdF9kYXRhXzkwJHN0YXR1cywgcHJlZGljdGlvbnNfSUdfOTApCnByaW50KGNvbmZ1c2lvbl9tYXRyaXhfSUdfOTApCmBgYApUaGUgY29uZnVzaW9uIG1hdHJpeCBmb3IgdGhlIHByb3ZpZGVkIGNvZGUgaW5kaWNhdGVzIHRoZSBmb2xsb3dpbmc6PGJyPgoKCeKAoglUcnVlIFBvc2l0aXZlIChUUCk6IDI4IGNhc2VzIHdlcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjcxOTcuPGJyPgoJ4oCiCUZhbHNlIFBvc2l0aXZlIChGUCk6IDExIGNhc2VzIHdlcmUgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIDAuMjY0NywgMSBjYXNlIGFzIDAuNDYsIDEgY2FzZSBhcyAwLjY0MjksIDIgY2FzZXMgYXMgMC4wNzA0MiwgYW5kIDIgY2FzZXMgYXMgMC4xODE4Ljxicj4KCeKAoglUcnVlIE5lZ2F0aXZlIChUTik6IDYgY2FzZXMgd2VyZSBjb3JyZWN0bHkgcHJlZGljdGVkIGFzIDAuMDcwNDIsIDIgY2FzZXMgYXMgMC4xODE4LCBhbmQgNSBjYXNlcyBhcyAwLjY0MjkuPGJyPgoJ4oCiCUZhbHNlIE5lZ2F0aXZlIChGTik6IDEgY2FzZSB3YXMgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIDAuMDcwNDIgYW5kIDUgY2FzZXMgYXMgMC4xODE4Ljxicj4KCQojIyMgIEEuNSkgRXZhbHVhdGlvbiBNZXRyaWNzCmBgYHtyfQptZXRyaWNzX0lHXzkwIDwtIGV2YWx1YXRlX21vZGVsKHByZWRpY3Rpb25zX0lHXzkwLCBsYWJlbHNfSUdfOTApCnByaW50KG1ldHJpY3NfSUdfOTApCmBgYArigKIgQWNjdXJhY3kgKDkuNTIlKTogVGhpcyBtZXRyaWMgcmVwcmVzZW50cyB0aGUgb3ZlcmFsbCBjb3JyZWN0bmVzcyBvZiB0aGUgbW9kZWzigJlzIHByZWRpY3Rpb25zLiBJbiB0aGlzIGNhc2UsIHRoZSBtb2RlbCBhY2hpZXZlZCBhbiBhY2N1cmFjeSBvZiA5LjUyJSwgaW5kaWNhdGluZyBhIGxvdyBsZXZlbCBvZiBjb3JyZWN0bmVzcy48YnI+CgrigKIgUHJlY2lzaW9uICg4NS43MSUpOiBQcmVjaXNpb24gbWVhc3VyZXMgdGhlIGFjY3VyYWN5IG9mIHBvc2l0aXZlIHByZWRpY3Rpb25zLiBJbiB0aGlzIGNvbnRleHQsIHRoZSBtb2RlbCBhY2hpZXZlZCBhIHByZWNpc2lvbiBvZiA4NS43MSUsIGluZGljYXRpbmcgdGhhdCB3aGVuIGl0IHByZWRpY3RlZCBhIHBvc2l0aXZlIG91dGNvbWUsIGl0IHdhcyBjb3JyZWN0IGluIDg1LjcxJSBvZiBjYXNlcy48YnI+CgrigKIgU2Vuc2l0aXZpdHkgKFJlY2FsbCkgKDc1JSk6IFNlbnNpdGl2aXR5LCBhbHNvIGtub3duIGFzIHJlY2FsbCwgbWVhc3VyZXMgdGhlIGFiaWxpdHkgb2YgdGhlIG1vZGVsIHRvIGNvcnJlY3RseSBpZGVudGlmeSBwb3NpdGl2ZSBpbnN0YW5jZXMuIEluIHRoaXMgY2FzZSwgdGhlIG1vZGVs4oCZcyBzZW5zaXRpdml0eSBpcyA3NSUsIHN1Z2dlc3RpbmcgYSBtb2RlcmF0ZSBwZXJmb3JtYW5jZSBpbiBjYXB0dXJpbmcgcG9zaXRpdmUgaW5zdGFuY2VzLjxicj4KCuKAoiBTcGVjaWZpY2l0eSAoMCUpOiBTcGVjaWZpY2l0eSBtZWFzdXJlcyB0aGUgYWJpbGl0eSBvZiB0aGUgbW9kZWwgdG8gY29ycmVjdGx5IGlkZW50aWZ5IG5lZ2F0aXZlIGluc3RhbmNlcy4gV2l0aCBhIHNwZWNpZmljaXR5IG9mIDAlLCB0aGUgbW9kZWwgZGlkIG5vdCBwZXJmb3JtIHdlbGwgaW4gYWNjdXJhdGVseSBpZGVudGlmeWluZyBuZWdhdGl2ZSBjYXNlcy48YnI+CgpJbiBzdW1tYXJ5LCB0aGUgbW9kZWwgZXhoaWJpdGVkIGxvdyBhY2N1cmFjeSBhbmQgc3BlY2lmaWNpdHkuIFRoZSBoaWdoIHByZWNpc2lvbiBhbmQgc2Vuc2l0aXZpdHkgc3VnZ2VzdCBhIHJlYXNvbmFibGUgYWJpbGl0eSB0byBtYWtlIGNvcnJlY3QgcHJlZGljdGlvbnMsIGVzcGVjaWFsbHkgaW4gY2FwdHVyaW5nIHBvc2l0aXZlIGluc3RhbmNlcy4KCiMjIEIpIEM1MCBBbGdvcml0aG06IEdhaW4gUmF0aW8KCiMjIEIuMSkgQ29uc3RydWN0aW5nIHRoZSBEZWNpc2lvbiBUcmVlCmBgYHtyfQpsaWJyYXJ5KEM1MCkKdHJhaW5fZGF0YV85MCRzdGF0dXMgPC0gYXMuZmFjdG9yKHRyYWluX2RhdGFfOTAkc3RhdHVzKQoKQzVGaXRfOTAgPC0gQzUuMChzdGF0dXMgfiAuLCBkYXRhID0gdHJhaW5fZGF0YV85MCwgY29udHJvbCA9IEM1LjBDb250cm9sKGVhcmx5U3RvcHBpbmcgPSBGQUxTRSwgQ0YgPSAwLjI1KSkKc3VtbWFyeShDNUZpdF85MCkKYGBgClRoZSBjb2RlIGJ1aWxkcyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbC4gCgojIyBCLjIpIFBsb3R0aW5nCmBgYHtyfQpwbG90KEM1Rml0XzkwLCB0eXBlID0gInNpbXBsZSIpCmBgYAoqKlJvb3QgTm9kZSAoTm9kZSAxKToqKjxicj4KJm5ic3A7Jm5ic3A74oCiCVNwbGl0dGluZyBhdHRyaWJ1dGU6IOKAnHJlbGF0aW9uc2hpcHPigJ0gPD0gMywgNDg3IGNhc2VzIHdpdGggMzQgZXJyb3JzICg3LjAlKS48YnI+CgoqKkJyYW5jaGVzOioqPGJyPgombmJzcDsmbmJzcDvigKIJTm9kZXMgMi01NTxicj4KCQoqKkxlYWYgTm9kZXMgKFRlcm1pbmFsIE5vZGVzKToqKgombmJzcDsmbmJzcDvigKIgTm9kZSAyODogVGVybWluYWwgbm9kZSBwcmVkaWN0aW5nIGNsYXNzIDAgKDEwMCUgY2xhc3MgMCwgMCUgY2xhc3MgMSkuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSAyOTogVGVybWluYWwgbm9kZSBwcmVkaWN0aW5nIGNsYXNzIDEgKDQyLjklIGNsYXNzIDAsIDU3LjElIGNsYXNzIDEpLjxicj4KCioqTnVtYmVyIG9mIE5vZGVzOioqIDcgKDEgcm9vdCwgNiBpbnRlcm5hbCwgMiBsZWFmKTxicj4KCioqTW9zdCBJbXBvcnRhbnQgRmVhdHVyZXM6Kio8YnI+CiZuYnNwOyZuYnNwOzEuIHJlbGF0aW9uc2hpcHM6IENyaXRpY2FsIGFzIHRoZSByb290IG5vZGUsIGluZGljYXRpbmcgaGlnaCBpbXBvcnRhbmNlLjxicj4KJm5ic3A7Jm5ic3A7Mi4gZnVuZGluZ190b3RhbF91c2Q6IFNpZ25pZmljYW50IGZvciBmdXJ0aGVyIHNwbGl0cywgc2hvd2Nhc2luZyBpdHMgcmVsZXZhbmNlLjxicj4KJm5ic3A7Jm5ic3A7My4gbWlsZXN0b25lczogVXNlZCBmb3IgYWRkaXRpb25hbCBzcGxpdHMsIGVtcGhhc2l6aW5nIGl0cyBjb250cmlidXRpb24uPGJyPgoKSW4gc3VtbWFyeSwgdGhpcyB0cmVlIHV0aWxpemVzIHRoZSDigJxyZWxhdGlvbnNoaXBz4oCdIGZlYXR1cmUgZm9yIHRoZSBpbml0aWFsIHNwbGl0LCBmb2xsb3dlZCBieSBhZGRpdGlvbmFsIGNyaXRlcmlhIGF0IGVhY2ggYnJhbmNoaW5nIHBvaW50LiBUaGlzIGhpZXJhcmNoaWNhbCBzdHJ1Y3R1cmUgbGVhZHMgdG8gcHJlZGljdGlvbnMgYXQgdGhlIHRlcm1pbmFsIG5vZGVzLCB3aGVyZSB0aGUgd2VpZ2h0cyBhc3NvY2lhdGVkIHdpdGggZWFjaCB0ZXJtaW5hbCBub2RlIGluZGljYXRlIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZhbGxpbmcgaW50byBlYWNoIGNhdGVnb3J5LiBUaGUgbW9zdCBpbXBvcnRhbnQgZmVhdHVyZXMgZm9yIGRlY2lzaW9uLW1ha2luZyBhcmUgcmVsYXRpb25zaGlwcywgZnVuZGluZ190b3RhbF91c2QsIGFuZCBtaWxlc3RvbmVzLiBJZiB5b3UgaGF2ZSBhbnkgZnVydGhlciBxdWVzdGlvbnMgb3IgaWYgdGhlcmXigJlzIGFueXRoaW5nIGVsc2UgSSBjYW4gYXNzaXN0IHlvdSB3aXRoLCBmZWVsIGZyZWUgdG8gbGV0IG1lIGtub3cuCgojIyBCLjMpIFRlc3RpbmcKYGBge3J9CnByZWRpY3Rpb25zX0dSXzkwIDwtIHByZWRpY3QoQzVGaXRfOTAsIG5ld2RhdGEgPSB0ZXN0X2RhdGFfOTApCmxhYmVsc19HUl85MCA8LSB0ZXN0X2RhdGFfOTAkc3RhdHVzCmBgYApUaGUgY29kZSBnZW5lcmF0ZXMgcHJlZGljdGlvbnMgZm9yIHRoZSB0ZXN0IGRhdGFzZXQgdXNpbmcgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwuCgojIyBCLjQpIENvbmZ1c2lvbiBNYXRyaXgKYGBge3J9CmNvbmZ1c2lvbl9tYXRyaXhfR1JfOTAgPC0gdGFibGUodGVzdF9kYXRhXzkwJHN0YXR1cywgcHJlZGljdGlvbnNfR1JfOTApCnByaW50KGNvbmZ1c2lvbl9tYXRyaXhfR1JfOTApCmBgYApUaGUgY29uZnVzaW9uIG1hdHJpeCBmb3IgdGhlIHByb3ZpZGVkIGNvZGUgaW5kaWNhdGVzIHRoZSBmb2xsb3dpbmc6PGJyPgoKCeKAoglUcnVlIFBvc2l0aXZlIChUUCk6IDI2IGNhc2VzIHdlcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcyAxLjxicj4KCeKAoglGYWxzZSBQb3NpdGl2ZSAoRlApOiA0IGNhc2VzIHdlcmUgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIDEsIGFuZCAxMSBjYXNlcyB3ZXJlIGluY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjxicj4KCeKAoglUcnVlIE5lZ2F0aXZlIChUTik6IDIyIGNhc2VzIHdlcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjxicj4KCeKAoglGYWxzZSBOZWdhdGl2ZSAoRk4pOiAxMSBjYXNlcyB3ZXJlIGluY29ycmVjdGx5IHByZWRpY3RlZCBhcyAwLjxicj4KCQojIyBCLjUpIEV2YWx1YXRpb24gTWV0cmljcwpgYGB7cn0KbWV0cmljc19HUl85MCA8LSBldmFsdWF0ZV9tb2RlbChwcmVkaWN0aW9uc19HUl85MCwgbGFiZWxzX0dSXzkwKQpgYGAK4oCiIEFjY3VyYWN5ICg3Ni4xOSUpOiBUaGlzIG1ldHJpYyByZXByZXNlbnRzIHRoZSBvdmVyYWxsIGNvcnJlY3RuZXNzIG9mIHRoZSBtb2RlbOKAmXMgcHJlZGljdGlvbnMuIEluIHRoaXMgY2FzZSwgdGhlIG1vZGVsIGFjaGlldmVkIGFuIGFjY3VyYWN5IG9mIDc2LjE5JSwgaW5kaWNhdGluZyBhIHJlbGF0aXZlbHkgaGlnaCBsZXZlbCBvZiBjb3JyZWN0bmVzcy48YnI+CgrigKIgUHJlY2lzaW9uICg2Ni42NyUpOiBQcmVjaXNpb24gbWVhc3VyZXMgdGhlIGFjY3VyYWN5IG9mIHBvc2l0aXZlIHByZWRpY3Rpb25zLiBJbiB0aGlzIGNvbnRleHQsIHRoZSBtb2RlbCBhY2hpZXZlZCBhIHByZWNpc2lvbiBvZiA2Ni42NyUsIGluZGljYXRpbmcgdGhhdCB3aGVuIGl0IHByZWRpY3RlZCBhIHBvc2l0aXZlIG91dGNvbWUsIGl0IHdhcyBjb3JyZWN0IGluIDY2LjY3JSBvZiBjYXNlcy48YnI+CgrigKIgU2Vuc2l0aXZpdHkgKFJlY2FsbCkgKDg0LjYyJSk6IFNlbnNpdGl2aXR5LCBhbHNvIGtub3duIGFzIHJlY2FsbCwgbWVhc3VyZXMgdGhlIGFiaWxpdHkgb2YgdGhlIG1vZGVsIHRvIGNvcnJlY3RseSBpZGVudGlmeSBwb3NpdGl2ZSBpbnN0YW5jZXMuIEluIHRoaXMgY2FzZSwgdGhlIG1vZGVs4oCZcyBzZW5zaXRpdml0eSBpcyA4NC42MiUsIHN1Z2dlc3RpbmcgYSBnb29kIHBlcmZvcm1hbmNlIGluIGNhcHR1cmluZyBwb3NpdGl2ZSBpbnN0YW5jZXMuPGJyPgoK4oCiIFNwZWNpZmljaXR5ICg3MC4yNyUpOiBTcGVjaWZpY2l0eSBtZWFzdXJlcyB0aGUgYWJpbGl0eSBvZiB0aGUgbW9kZWwgdG8gY29ycmVjdGx5IGlkZW50aWZ5IG5lZ2F0aXZlIGluc3RhbmNlcy4gV2l0aCBhIHNwZWNpZmljaXR5IG9mIDcwLjI3JSwgdGhlIG1vZGVsIHBlcmZvcm1lZCBtb2RlcmF0ZWx5IHdlbGwgaW4gYWNjdXJhdGVseSBpZGVudGlmeWluZyBuZWdhdGl2ZSBjYXNlcy48YnI+CgpJbiBzdW1tYXJ5LCB0aGUgbW9kZWwgZXhoaWJpdGVkIGhpZ2ggYWNjdXJhY3kgYW5kIHNlbnNpdGl2aXR5LiBUaGUgcHJlY2lzaW9uIGFuZCBzcGVjaWZpY2l0eSBzdWdnZXN0IGEgcmVhc29uYWJsZSBhYmlsaXR5IHRvIG1ha2UgY29ycmVjdCBwcmVkaWN0aW9ucywgYm90aCBpbiBjYXB0dXJpbmcgcG9zaXRpdmUgaW5zdGFuY2VzIGFuZCBpZGVudGlmeWluZyBuZWdhdGl2ZSBpbnN0YW5jZXMuCgoKIyMgQykgQ0FSVCBBbGdvcml0aG06IEdpbmkgSW5kZXgKCiMjIEMuMSkgQ29uc3RydWN0aW5nIHRoZSBEZWNpc2lvbiBUcmVlCgpgYGB7cn0KbGlicmFyeShycGFydCkKcHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfR0lfOTAgPC0gcnBhcnQoc3RhdHVzIH4gLiwgZGF0YSA9IHRyYWluX2RhdGFfOTAsIG1ldGhvZCA9ICJjbGFzcyIsIHBhcm1zID0gbGlzdChzcGxpdCA9ICJnaW5pIikpCmBgYApUaGUgY29kZSBidWlsZHMgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwuIAoKIyMgQy4yKSBQbG90dGluZwpgYGB7cn0KcHJpbnQocHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfR0lfOTApCnJwYXJ0LnBsb3QocHJlcHJvY2Vzc2VkX2RhdGFzZXRfY3RyZWVfR0lfOTApCmBgYAoqKlJvb3QgTm9kZSAoTm9kZSAxKToqKjxicj4KJm5ic3A7Jm5ic3A74oCiIFNwbGl0dGluZyBhdHRyaWJ1dGU6IOKAnHJlbGF0aW9uc2hpcHPigJ0gPCAzLjUsIHByZWRpY3RpbmcgY2xhc3MgMCAoNTEuMSUgY2xhc3MgMCwgNDguOSUgY2xhc3MgMSkuPGJyPgoKKipCcmFuY2hlczoqKjxicj4KJm5ic3A7Jm5ic3A74oCiIE5vZGUgMjogSWYg4oCccmVsYXRpb25zaGlwc+KAnSA8IDMuNSwgcHJlZGljdHMgY2xhc3MgMCAoNzguMSUgY2xhc3MgMCwgMjEuOSUgY2xhc3MgMSkuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA0OiBJZiDigJxhZ2VfbGFzdF9taWxlc3RvbmVfeWVhcuKAnSA8IDUuNSwgcHJlZGljdHMgY2xhc3MgMCAoODEuOSUgY2xhc3MgMCwgMTguMSUgY2xhc3MgMSkuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA4OiBJZiDigJxtaWxlc3RvbmVz4oCdIDwgMi41LCBwcmVkaWN0cyBjbGFzcyAwICg4NS4wJSBjbGFzcyAwLCAxNS4wJSBjbGFzcyAxKS48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDk6IElmIOKAnG1pbGVzdG9uZXPigJ0gPj0gMi41LCBwcmVkaWN0cyBjbGFzcyAxICgzNi40JSBjbGFzcyAwLCA2My42JSBjbGFzcyAxKS48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDU6IElmIOKAnGFnZV9sYXN0X21pbGVzdG9uZV95ZWFy4oCdID49IDUuNSwgcHJlZGljdHMgY2xhc3MgMSAoMzcuNSUgY2xhc3MgMCwgNjIuNSUgY2xhc3MgMSkuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSAzOiBJZiDigJxyZWxhdGlvbnNoaXBz4oCdID49IDMuNSwgcHJlZGljdHMgY2xhc3MgMSAoMzQuMyUgY2xhc3MgMCwgNjUuNyUgY2xhc3MgMSkuPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA2OiBJZiDigJxhZ2VfbGFzdF9mdW5kaW5nX3llYXLigJ0gPCAwLjUsIHByZWRpY3RzIGNsYXNzIDAgKDYxLjIlIGNsYXNzIDAsIDM4LjglIGNsYXNzIDEpLjxicj4KJm5ic3A7Jm5ic3A74oCiIE5vZGUgMTI6IElmIOKAnG1pbGVzdG9uZXPigJ0gPCAyLjUsIHByZWRpY3RzIGNsYXNzIDAgKDgzLjklIGNsYXNzIDAsIDE2LjElIGNsYXNzIDEpLjxicj4KJm5ic3A7Jm5ic3A74oCiIE5vZGUgMTM6IElmIOKAnG1pbGVzdG9uZXPigJ0gPj0gMi41LCBwcmVkaWN0cyBjbGFzcyAxICgyMi4yJSBjbGFzcyAwLCA3Ny44JSBjbGFzcyAxKS48YnI+CiZuYnNwOyZuYnNwO+KAoiBOb2RlIDc6IElmIOKAnGFnZV9sYXN0X2Z1bmRpbmdfeWVhcuKAnSA+PSAwLjUsIHByZWRpY3RzIGNsYXNzIDEgKDI5LjElIGNsYXNzIDAsIDcwLjklIGNsYXNzIDEpLjxicj4KCioqTGVhZiBOb2RlcyAoVGVybWluYWwgTm9kZXMpOioqPGJyPgombmJzcDsmbmJzcDvigKIgTm9kZSA4OiBQcmVkaWN0cyBjbGFzcyAwICgxMDAlIGNsYXNzIDAsIDAlIGNsYXNzIDEpLjxicj4KJm5ic3A7Jm5ic3A74oCiIE5vZGUgOTogUHJlZGljdHMgY2xhc3MgMSAoNDIuOSUgY2xhc3MgMCwgNTcuMSUgY2xhc3MgMSkuPGJyPgoKKipOdW1iZXIgb2YgTm9kZXM6KiogMTMgKDEgcm9vdCwgMTEgaW50ZXJuYWwsIDIgbGVhZik8YnI+CgoqKk1vc3QgSW1wb3J0YW50IEZlYXR1cmVzOioqPGJyPgombmJzcDsmbmJzcDsxLiByZWxhdGlvbnNoaXBzOiBDcml0aWNhbCBmb3IgdGhlIGluaXRpYWwgc3BsaXQsIGluZGljYXRpbmcgaGlnaCBpbXBvcnRhbmNlLjxicj4KJm5ic3A7Jm5ic3A7Mi4gYWdlX2xhc3RfbWlsZXN0b25lX3llYXI6IEltcG9ydGFudCBmb3IgZnVydGhlciBzcGxpdHMuPGJyPgombmJzcDsmbmJzcDszLiBtaWxlc3RvbmVzOiBTaWduaWZpY2FudCBmb3IgYWRkaXRpb25hbCBkaXN0aW5jdGlvbnMuPGJyPgoKSW4gc3VtbWFyeSwgdGhlIHRyZWUgYmVnaW5zIHdpdGgg4oCccmVsYXRpb25zaGlwc+KAnSBhcyB0aGUga2V5IGZhY3RvciwgZm9sbG93ZWQgYnkgY3JpdGVyaWEgbGlrZSDigJxhZ2VfbGFzdF9taWxlc3RvbmVfeWVhcuKAnSBhbmQg4oCcbWlsZXN0b25lcy7igJ0gSXQgZ3VpZGVzIHByZWRpY3Rpb25zIGF0IHRoZSB0ZXJtaW5hbCBub2Rlcywgd2hlcmUgd2VpZ2h0cyBzaWduaWZ5IG9ic2VydmF0aW9uIGNvdW50cy4gQ3JpdGljYWwgZmVhdHVyZXMgYXJlIHJlbGF0aW9uc2hpcHMsIGFnZV9sYXN0X21pbGVzdG9uZV95ZWFyLCBhbmQgbWlsZXN0b25lcy4KCiMjIEMuMykgVGVzdGluZwpgYGB7cn0KcHJlZGljdGlvbnNfR0lfOTAgPC0gcHJlZGljdChwcmVwcm9jZXNzZWRfZGF0YXNldF9jdHJlZV9HSV85MCwgbmV3ZGF0YSA9IHRlc3RfZGF0YV85MCwgdHlwZSA9ICJjbGFzcyIpCmxhYmVsc19HSV85MCA8LSB0ZXN0X2RhdGFfOTAkc3RhdHVzCmBgYApUaGUgY29kZSBnZW5lcmF0ZXMgcHJlZGljdGlvbnMgZm9yIHRoZSB0ZXN0IGRhdGFzZXQgdXNpbmcgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwuCgojIyBDLjQpIENvbmZ1c2lvbiBNYXRyaXgKYGBge3J9CmNvbmZ1c2lvbl9tYXRyaXhfR0lfOTAgPC0gdGFibGUodGVzdF9kYXRhXzkwJHN0YXR1cywgcHJlZGljdGlvbnNfR0lfOTApCnByaW50KGNvbmZ1c2lvbl9tYXRyaXhfR0lfOTApCmBgYApUaGUgY29uZnVzaW9uIG1hdHJpeCBmb3IgdGhlIHByb3ZpZGVkIGNvZGUgaW5kaWNhdGVzIHRoZSBmb2xsb3dpbmc6PGJyPgoKCeKAoglUcnVlIFBvc2l0aXZlIChUUCk6IDMxIGNhc2VzIHdlcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcyAxLjxicj4KCeKAoglGYWxzZSBQb3NpdGl2ZSAoRlApOiA4IGNhc2VzIHdlcmUgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIDEsIGFuZCA2IGNhc2VzIHdlcmUgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIDAuPGJyPgoJ4oCiCVRydWUgTmVnYXRpdmUgKFROKTogMTggY2FzZXMgd2VyZSBjb3JyZWN0bHkgcHJlZGljdGVkIGFzIDAuPGJyPgoJ4oCiCUZhbHNlIE5lZ2F0aXZlIChGTik6IDYgY2FzZXMgd2VyZSBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgYXMgMC48YnI+CgojIyBDLjUpIEV2YWx1YXRpb24gTWV0cmljcwpgYGB7cn0KbWV0cmljc19HSV85MCA8LSBldmFsdWF0ZV9tb2RlbChwcmVkaWN0aW9uc19HSV85MCwgbGFiZWxzX0dJXzkwKQpgYGAK4oCiIEFjY3VyYWN5ICg3Ny43OCUpOiBUaGlzIG1ldHJpYyByZXByZXNlbnRzIHRoZSBvdmVyYWxsIGNvcnJlY3RuZXNzIG9mIHRoZSBtb2RlbOKAmXMgcHJlZGljdGlvbnMuIEluIHRoaXMgY2FzZSwgdGhlIG1vZGVsIGFjaGlldmVkIGFuIGFjY3VyYWN5IG9mIDc3Ljc4JSwgaW5kaWNhdGluZyBhIHJlbGF0aXZlbHkgaGlnaCBsZXZlbCBvZiBjb3JyZWN0bmVzcy48YnI+CgrigKIgUHJlY2lzaW9uICg3NSUpOiBQcmVjaXNpb24gbWVhc3VyZXMgdGhlIGFjY3VyYWN5IG9mIHBvc2l0aXZlIHByZWRpY3Rpb25zLiBJbiB0aGlzIGNvbnRleHQsIHRoZSBtb2RlbCBhY2hpZXZlZCBhIHByZWNpc2lvbiBvZiA3NSUsIGluZGljYXRpbmcgdGhhdCB3aGVuIGl0IHByZWRpY3RlZCBhIHBvc2l0aXZlIG91dGNvbWUsIGl0IHdhcyBjb3JyZWN0IGluIDc1JSBvZiBjYXNlcy48YnI+CgrigKIgU2Vuc2l0aXZpdHkgKFJlY2FsbCkgKDY5LjIzJSk6IFNlbnNpdGl2aXR5LCBhbHNvIGtub3duIGFzIHJlY2FsbCwgbWVhc3VyZXMgdGhlIGFiaWxpdHkgb2YgdGhlIG1vZGVsIHRvIGNvcnJlY3RseSBpZGVudGlmeSBwb3NpdGl2ZSBpbnN0YW5jZXMuIEluIHRoaXMgY2FzZSwgdGhlIG1vZGVs4oCZcyBzZW5zaXRpdml0eSBpcyA2OS4yMyUsIHN1Z2dlc3RpbmcgYSBtb2RlcmF0ZSBwZXJmb3JtYW5jZSBpbiBjYXB0dXJpbmcgcG9zaXRpdmUgaW5zdGFuY2VzLjxicj4KCuKAoiBTcGVjaWZpY2l0eSAoODMuNzglKTogU3BlY2lmaWNpdHkgbWVhc3VyZXMgdGhlIGFiaWxpdHkgb2YgdGhlIG1vZGVsIHRvIGNvcnJlY3RseSBpZGVudGlmeSBuZWdhdGl2ZSBpbnN0YW5jZXMuIFdpdGggYSBzcGVjaWZpY2l0eSBvZiA4My43OCUsIHRoZSBtb2RlbCBwZXJmb3JtZWQgd2VsbCBpbiBhY2N1cmF0ZWx5IGlkZW50aWZ5aW5nIG5lZ2F0aXZlIGNhc2VzLjxicj4KCkluIHN1bW1hcnksIHRoZSBtb2RlbCBleGhpYml0ZWQgaGlnaCBhY2N1cmFjeSBhbmQgc3BlY2lmaWNpdHkuIFRoZSBwcmVjaXNpb24gYW5kIHNlbnNpdGl2aXR5IHN1Z2dlc3QgYSByZWFzb25hYmxlIGFiaWxpdHkgdG8gbWFrZSBjb3JyZWN0IHByZWRpY3Rpb25zLCBib3RoIGluIGNhcHR1cmluZyBwb3NpdGl2ZSBpbnN0YW5jZXMgYW5kIGlkZW50aWZ5aW5nIG5lZ2F0aXZlIGluc3RhbmNlcy48YnI+CgoKCiMjIDUuMi1DbHVzdGVyaW5nIAoKQ29udHJhcnkgdG8gY2xhc3NpZmljYXRpb24sICoqY2x1c3RlcmluZyBpcyBhIGZvcm0gb2YgdW5zdXBlcnZpc2VkIGxlYXJuaW5nLCB3aGVyZSB0aGVyZSBpcyBubyBwcmVkZWZpbmVkIGNsYXNzIGxhYmVsKiouIEhvd2V2ZXIsIGluIHRoZSBzdGFydHVwIGRhdGEsIHRoZSBjbGFzcyBhdHRyaWJ1dGUgaXMgYWxyZWFkeSBrbm93biB3aGljaCBpcyB0aGUgInN0YXR1cyIuIFN0YXR1cyBhdHRyaWJ1dGUgaG9sZHMgdHdvIHZhbHVlcywgZWl0aGVyICJhY3F1aXJlZCIgc3RhdHVzLCBvciAiY2xvc2VkIiBzdGF0dXMuIElmIHRoZSBjbGFzcyBhdHRyaWJ1dGUgaXMga25vd24sIHRoZW4gd2hhdCBpcyB0aGUgdXNlIGNsdXN0ZXJpbmc/IEl0IGlzIHN0aWxsIGJlbmVmaWNpYWwgZm9yIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMgdG8gZGlzY292ZXIgdGhlIHVuc2VlbiBzdHJ1Y3R1cmUgb3IgcGF0dGVybiBpbiB0aGUgZGF0YSBzZXQsIGZpbmRpbmdzIG9mIGFub21hbGllcywgdmlzdWFsaXppbmcgdGhlIGRhdGEgc2V0IGFuZCBldmVuIGFzc2Vzc2luZyB0aGUgcXVhbGl0eSBvZiB0aGUgY2x1c3RlcmluZyBhbGdvcml0aG0uCgpXZSB3aWxsIHVzZSAqKmstbWVhbnMgYXMgb3VyIHBhcnRpdGlvbmluZyBtZXRob2QqKi4gSy1tZWFucyBjbHVzdGVyaW5nIGlzIHJlY29tbWVuZGVkIGZvciBpdHMgc2ltcGxpY2l0eSwgZWZmaWNpZW5jeSwgYW5kIGVmZmVjdGl2ZW5lc3MgaW4gcGFydGl0aW9uaW5nIGRhdGEgaW50byBkaXN0aW5jdCBncm91cHMgYmFzZWQgb24gc2ltaWxhcml0aWVzLiBJdOKAmXMgY29tcHV0YXRpb25hbGx5IGVmZmljaWVudC4gVGhlIG1haW4gZ29hbCBpcyB0byBwYXJ0aXRpb24gaW50byBrIG51bWJlcnMgb2YgY2x1c3RlcnMsIHdoZXJlIGVhY2ggZGF0YSBwb2ludCBiZWxvbmdzIHRvIHRoZSBjbHVzdGVyIHdpdGggdGhlIG5lYXJlc3QgbWVhbnMuICoqV2UgY2hvc2Ugaz0gMiwgMyBhbmQgNCoqIHRvIHNlZSB3aGF0IGFyZSB0aGUgZGlmZmVyZW5jZXMgaW4gdGhlIHBhdHRlcm5zIG9mIGVhY2ggayBjbHVzdGVycyBhbmQgd2h5IHRoZWlyIHF1YWxpdGllcyBkaWZmZXIgZnJvbSBlYWNoIG90aGVyLgoKYGBge3J9CmxpYnJhcnkocmVhZHhsKQpwcmVwcm9jZXNzZWRfZGF0YXNldCA8LSByZWFkX2V4Y2VsKCJQcmVwcm9jZXNzZWRfU3RhcnR1cERhdGEueGxzeCIpCmBgYApUbyB3b3JrIG9uIHRoZSBwcmVwcm9jZXNzZWQgZGF0YXNldC4gCgpgYGB7cn0KbGlicmFyeShkcGx5cikKcHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMgPSBwcmVwcm9jZXNzZWRfZGF0YXNldCAlPiUgc2VsZWN0KGFnZV9maXJzdF9mdW5kaW5nX3llYXIsYWdlX2xhc3RfZnVuZGluZ195ZWFyLGFnZV9maXJzdF9taWxlc3RvbmVfeWVhcixhZ2VfbGFzdF9taWxlc3RvbmVfeWVhcixyZWxhdGlvbnNoaXBzLGZ1bmRpbmdfcm91bmRzLGZ1bmRpbmdfdG90YWxfdXNkLG1pbGVzdG9uZXMsaGFzX1ZDLGhhc19hbmdlbCxoYXNfcm91bmRBLGhhc19yb3VuZEIsaGFzX3JvdW5kQyxoYXNfcm91bmRELGF2Z19wYXJ0aWNpcGFudHMsaXNfdG9wNTAwKQpWaWV3KHByZXByb2Nlc3NlZF9kYXRhc2V0LmZlYXR1cmVzKQpgYGAKRmlyc3QsIHdlIHNlbGVjdGVkIGFuZCB0cmFuc2ZlcnJlZCBzb21lIGNvbHVtbnMgZnJvbSBkYXRhc2V0IHRvIGFub3RoZXIgZGF0YXNldCAocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMpIHRvIG1ha2UgaXQgZWFzaWVyIHRvIGNsdXN0ZXIuCgpgYGB7cn0KIyBSdW4gay1tZWFucyB0byBmaW5kIGRpZmZlcmVudCBudW1iZXIgb2YgY2x1c3RlcnMgYWZ0ZXIgb21pdHRpbmcgTkEKCmxpYnJhcnkoQ2x1c3RlclIpCmxpYnJhcnkoY2x1c3RlcikKc2V0LnNlZWQoNTAwKQprbWVhblJlc3VsdHM8LWttZWFucyhuYS5vbWl0KHByZXByb2Nlc3NlZF9kYXRhc2V0LmZlYXR1cmVzKSwgMikKa21lYW5SZXN1bHRzCgprbWVhblJlc3VsdHMyPC1rbWVhbnMobmEub21pdChwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcyksIDMpCmttZWFuUmVzdWx0czIKCmttZWFuUmVzdWx0czM8LWttZWFucyhuYS5vbWl0KHByZXByb2Nlc3NlZF9kYXRhc2V0LmZlYXR1cmVzKSwgNCkKa21lYW5SZXN1bHRzMwpgYGAKQXMgc2FpZCwgdGhlIHByZXZpb3VzIGNvZGVzIGFyZSB0byBmaW5kIGstbWVhbiBjbHVzdGVycy4gaz0gMiwgMywgNCAoc2hvd24gaW4gZGlmZmVyZW50IGNvbG9yIHJlZCBpbiB0aGUgY29kZSkuIEluIHRoZSBuZXh0IGNvZGUgc25pcHBldCwgeW91IGNhbiB2aXN1YWxpemUgYWxsIHRoZSAyLW1lYW5zLCAzLW1lYW5zIGFuZCA0LW1lYW5zIGNsdXN0ZXJzLgoKYGBge3J9CiNWaXN1YWxpemF0aW9uIG9mIGFsbCB0aGUgY2x1c3RlcnMgd2UgbWFkZSBpbiB0aGUgcHJldmlvdXMgY29kZSBzbmlwcGV0LiAKCmxpYnJhcnkoZmFjdG9leHRyYSkKZnZpel9jbHVzdGVyKGttZWFuUmVzdWx0cywgZGF0YSA9IG5hLm9taXQocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMpKQoKZnZpel9jbHVzdGVyKGttZWFuUmVzdWx0czIsIGRhdGEgPSBuYS5vbWl0KHByZXByb2Nlc3NlZF9kYXRhc2V0LmZlYXR1cmVzKSkKCmZ2aXpfY2x1c3RlcihrbWVhblJlc3VsdHMzLCBkYXRhID0gbmEub21pdChwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcykpCmBgYApBcyBzaG93biwgYWxsIDItbWVhbnMsIDMtbWVhbnMgYW5kIDQtbWVhbnMgY2x1c3RlcnMgYXJlIG92ZXJsYXBwaW5nLiBDbHVzdGVycyBtYXkgb3ZlcmxhcCB3aGVuIHRoZSBkYXRhIGRvZXNu4oCZdCBoYXZlIGNsZWFyIGJvdW5kYXJpZXMgb3Igd2hlbiBjaG9zZW4gZmVhdHVyZXMgc3RydWdnbGUgdG8gZGlzdGluZ3Vpc2ggZ3JvdXBzIGVmZmVjdGl2ZWx5LiBJZiBjbHVzdGVycyBhcmUgY2xvc2VseSByZWxhdGVkIG9yIHNoYXJlIHNpbWlsYXJpdGllcywgdHJhZGl0aW9uYWwgbWV0aG9kcyBtYXkgZmluZCBpdCBoYXJkIHRvIGNyZWF0ZSBkaXN0aW5jdCBkaXZpc2lvbnMuIFRoZSBzZW5zaXRpdml0eSBvZiBhbGdvcml0aG1zIHRvIGluaXRpYWwgY29uZGl0aW9ucyBhbmQgdGhlIHN1YmplY3RpdmUgbmF0dXJlIG9mIGRlZmluaW5nIGNsdXN0ZXJzIGNhbiBhbHNvIGNvbnRyaWJ1dGUgdG8gb3ZlcmxhcHMuIEVzc2VudGlhbGx5LCBvdmVybGFwcGluZyBjbHVzdGVycyByZXZlYWwgdGhlIGNvbXBsZXhpdHkgaW4gdGhlIGRhdGEsIGluZGljYXRpbmcgdGhlIG5lZWQgZm9yIGFsdGVybmF0aXZlIG1ldGhvZHMgb3IgYSByZWV2YWx1YXRpb24gb2YgZmVhdHVyZSBjaG9pY2VzIHRvIGJldHRlciBjYXB0dXJlIHVuZGVybHlpbmcgcGF0dGVybnMuIAoKIyMjIEEpIFNpbGhvdXR0ZSBNZXRob2QKClNpbGhvdWV0dGUgbWV0aG9kIGlzIGFuIHVuc3VwZXJ2aXNlZCBtZXRob2QgdG8gYXNzZXNzIHRoZSBxdWFsaXR5IG9mIHRoZSBjbHVzdGVycy4gSWYgYSBzaWxob3VldHRlIHNjb3JlIGlzIFw+PTAuNSwgaXQgbWVhbnMgdGhlIGNsdXN0ZXJpbmcgaXMgZmFpcmx5IGdvb2QuIElkZWFsIGNsdXN0ZXJpbmcgaGFzIHNpbGhvdWV0dGUgc2NvcmUgPSAxIHdoaWxlIGlmIHRoZSBzaWxob3VldHRlIHNjb3JlIGlzIGxlc3MgdGhhbiAwLCBpdCBtZWFucyB3cm9uZyBjbHVzdGVyaW5nIGFuZCB0aGUgc2FtcGxlIGlzIGFzc2lnbmVkIHRvIHRoZSB3cm9uZyBjbHVzdGVyLiBUaGUgU2lsaG91ZXR0ZSBDb2VmZmljaWVudCBpcyBjYWxjdWxhdGVkIHVzaW5nIHRoZSBtZWFuIGludHJhLWNsdXN0ZXIgZGlzdGFuY2UgKGEpIGFuZCB0aGUgbWVhbiBuZWFyZXN0LWNsdXN0ZXIgZGlzdGFuY2UgKGIpIGZvciBlYWNoIHNhbXBsZS4gVGhlIFNpbGhvdWV0dGUgc2NvcmUgZm9yIGEgc2FtcGxlIGlzIChiIC0gYSkgLyBtYXgoYSxiKS4gVGhlIFNpbGhvdWV0dGUgd2lkdGggaXMgdGhlIGF2ZXJhZ2Ugb2YgU2lsaG91ZXR0ZSBDb2VmZmljaWVudCBhY2Nyb3NzIGFsbCBkYXRhIHBvaW50cyBpbiBhIGRhdGFzZXQgYW5kIGlzIHNvbWV0aW1lcyB1c2VkIGludGVyY2hhbmdlYWJseSB3aXRoIGVhY2ggb3RoZXIuCgpgYGB7cn0KI1NpbGhvdWV0dGUgbWV0aG9kCiNGb3IgMi1tZWFuIGNsdXN0ZXIgYW5kIHRoZWlyIHBsb3RzCnNpbGhvdWV0dGVfc2NvcmVzPC1zaWxob3VldHRlKGttZWFuUmVzdWx0cyRjbHVzdGVyLCBkaXN0KG5hLm9taXQocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMpKSkKcGxvdChzaWxob3VldHRlX3Njb3JlcykKCiNGb3IgMy1tZWFuIGNsdXN0ZXIgYW5kIHRoZWlyIHBsb3RzCnNpbGhvdWV0dGVfc2NvcmVzPC1zaWxob3VldHRlKGttZWFuUmVzdWx0czIkY2x1c3RlciwgZGlzdChuYS5vbWl0KHByZXByb2Nlc3NlZF9kYXRhc2V0LmZlYXR1cmVzKSkpCnBsb3Qoc2lsaG91ZXR0ZV9zY29yZXMpCgojRm9yIDQtbWVhbiBjbHVzdGVyIGFuZCB0aGVpciBwbG90cwpzaWxob3VldHRlX3Njb3Jlczwtc2lsaG91ZXR0ZShrbWVhblJlc3VsdHMzJGNsdXN0ZXIsIGRpc3QobmEub21pdChwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcykpKQpwbG90KHNpbGhvdWV0dGVfc2NvcmVzKQoKI1RoaXMgaXMgdG8gcGxvdCB0aGUgb3B0aW1hbCBLLWNsdXN0ZXJzLgpmdml6X25iY2x1c3QobmEub21pdChwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcyksIGttZWFucywgbWV0aG9kID0gInNpbGhvdWV0dGUiKQpgYGAKCkFzIHlvdSBjYW4gb24gdG9wLCBlYWNoIHNpbGhvdWV0dGUgd2lkdGggZGVub3RlZCBieSBTaSBvZiBhIGstbWVhbnMgY2x1c3RlciBpcyBhdmVyYWdlZCAoQXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIHNob3duIGluIHRoZSBzaWxob3VldHRlIHBsb3QpIGFuZCBwbG90dGVkIG9uIHRvIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVyIHBsb3QuCgpGb3IgZXhhbXBsZSwgaW4gdGhlIHNpbGhvdWV0dGUgcGxvdCBmb3IgMi1tZWFucyBjbHVzdGVyLCB0aGUgc2lsaG91ZXR0ZSBjb2VmZmljaWVudCAob3Igd2lkdGgpIGlzIDAuMjkgZm9yIG9uZSBjbHVzdGVyIGFuZCAwLjM0IGZvciB0aGUgb3RoZXIgY2x1c3Rlci4gV2hlbiB0aGV5IGFyZSBhdmVyYWdlZCwgQXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGlzIGFib3V0IDAuMzMuIFRoZSBzYW1lIGdvZXMgZm9yIHNpbGhvdWV0dGUgcGxvdHMgZm9yIDMtbWVhbnMgY2x1c3RlciAoQXZlcmFnZSBzaWxob3VldHRlIHdpZHRoID0gKDAuMjkrMC4zMSswLjIxKS8zPSAwLjI3KSBhbmQgNC1tZWFucyBjbHVzdGVyIChBdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggPSAoMC4yMiswLjE3KzAuMTkrMC4yOCkvND0gMC4yMikuIFRoaXMgaXMgZG9uZSBmb3IgbWFueSBvdGhlciBrIGNsdXN0ZXJzLiBUaGVuIHRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggaXMgcGxvdHRlZCBhY2NvcmRpbmdseSBvbnRvIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyBncmFwaC4gVGhlIGhpZ2hlc3Qgc2lsaG91ZXR0ZSB3aWR0aCBpbmRpY2F0ZXMgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGJlY2F1c2UgaXQgaXMgdGhlIGxlYXN0IG92ZXJsYXBwaW5nLiBJbiBvdXIgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMgZ3JhcGgsIGl0IHNob3dzIHRoYXQgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMgaXMgMi4gQXMgeW91IGNhbiBzZWUgd2l0aCB0aGUgdmFsdWVzIG9mIHRoZSBzaWxob3VldHRlIHNjb3JlcywgdGhleSBhcmUgYWxsIGxlc3MgdGhhbiAwLjUgYnV0IGdyZWF0ZXIgdGhhbiAwLCB3aGljaCBtZWFucyB0aGUgY2x1c3RlcnMgbXVzdCBiZSB2ZXJ5IG92ZXJsYXBwaW5nIGp1c3QgbGlrZSBob3cgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyBhcmUgc2hvd24uCgojIyMgQikgVG90YWwgV2l0aGluLUNsdXN0ZXIgU3VtIG9mIFNxdWFyZQoKVGhlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmUgKFdTUykgaXMgdGhlIHN1bSBvZiBzcXVhcmVkIGRpc3RhbmNlcyBiZXR3ZWVuIGVhY2ggZGF0YSBwb2ludCBpbiBhIGNsdXN0ZXIgYW5kIHRoZSBjZW50cm9pZCBvZiB0aGF0IGNsdXN0ZXIsIGFuZCB0aGVuIHN1bSBvZiB0aGVzZSB2YWx1ZXMgYWNyb3NzIGFsbCBjbHVzdGVycy4gVGhlIG9iamVjdGl2ZSBvZiB0aGUgay1tZWFucyBhbGdvcml0aG0gaXMgdG8gZmluZCBjbHVzdGVyIGFzc2lnbm1lbnRzIGFuZCBjZW50cm9pZHMgdGhhdCBtaW5pbWl6ZSB0aGlzIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzLlRvdGFsIFdpdGhpbi1DbHVzdGVyIGlzIGZvdW5kIHRvIGJlIHBsb3R0ZWQgb24gYSBncmFwaCB0byBmaW5kIHRoZSBvcHRpbWFsIG51bWJlcnMgb2YgY2x1c3RlciB3aGVyZSB0aGUgdHVybmluZyBwb2ludCBpcy4gVGhpcyBtZXRob2QgaXMgY2FsbGVkIGFuIEVsYm93IE1ldGhvZC4KCmBgYHtyfQojVG90YWwgV2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMKI0ZvciAyLW1lYW4gY2x1c3RlcgprbWVhblJlc3VsdHMkdG90LndpdGhpbnNzCiNGb3IgMy1tZWFuIGNsdXN0ZXIKa21lYW5SZXN1bHRzMiR0b3Qud2l0aGluc3MKI0ZvciA0LW1lYW4gY2x1c3RlcgprbWVhblJlc3VsdHMzJHRvdC53aXRoaW5zcwoKI0VsYm93IG1ldGhvZCBmb3VuZCBieSBwbG90dGluZyB0b3RhbCBXU1MgZm9yIGVhY2ggay1tZWFuIGNsdXN0ZXJzLgpmdml6X25iY2x1c3QobmEub21pdChwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcyksIGttZWFucywgbWV0aG9kID0gIndzcyIpCmBgYAoKSW4gdGhlIGdyYXBoIGFib3ZlLCBpdCBzaG93cyB0aGF0IHRoZSB0dXJuaW5nIHBvaW50IGlzIDIgd2hpY2ggYWxzbyBzaG93cyB0aGF0IDItbWVhbiBjbHVzdGVycyBpcyB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMuIFRoZSB0b3RhbCBzdW0gb2Ygd2l0aGluIGlzIDIwNzc5Ljg0IGFuZCB3ZSBjYW4gc2VlIGl0IGRlY3JlYXNlcyBmcm9tIHRoYXQgcG9pbnQgbWFraW5nIGl0IHRoZSB0dXJuaW5nIHBvaW50IG9mIHRoaXMgZ3JhcGguIEFnYWluLCB0aGlzIGlzIGNhbGxlZCBFbGJvdyBtZXRob2QuCgojIyMgQykgQkN1YmVkIFByZWNpc2lvbiBhbmQgUmVjYWxsCgpQcmVjaXNpb24gaW5kaWNhdGVzIHRoZSBwdXJpdHkgb2YgYSBjbHVzdGVyLiBUaGUgaGlnaGVyIHRoZSBwcmVjaXNpb24sIHRoZSBoaWdoZXIgdGhlIHB1cml0eSBvZiB0aGUgY2x1c3Rlci4gUmVjYWxsIGluZGljYXRlcyBob3cgZ29vZCB0aGUgZGF0YSBwb2ludHMgb2Ygc2FtZSB0cnVlIGNsYXNzIGFyZSBwdXQgaW50byBzYW1lIGNsdXN0ZXIuIEJlbG93IHNob3dzIHRoZSBzdGVwcyB0byBwcmVwYXJlIGZvciBjYWxjdWxhdGluZyBCQ3ViZWQgUHJlY2lzaW9uIGFuZCBSZWNhbGwuCgpgYGB7cn0KCiNBZGQgc3RhdHVzIGJhY2sgdG8gRGF0YS5mZWF0dXJlcyBhcyB3ZSByZW1vdmVkIGl0IGZvciBlYXNpbmVzcwpwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcz1iaW5kX2NvbHMocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMscHJlcHJvY2Vzc2VkX2RhdGFzZXRbJ3N0YXR1cyddKQoKI09taXR0aW5nIE5BIHJvd3Mgc2luY2Ugd2UgY2x1c3RlcmVkIHdpdGggTkEgdmFsdWVzCnByZXByb2Nlc3NlZF9kYXRhc2V0LmZlYXR1cmVzPW5hLm9taXQocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMpCgojRmluZCBCQ3ViZWQgUHJlY2lzaW9uIGFuZCBSZWNhbGwKCiMxLSBGaW5kIG51bWJlciBvZiBpdGVtIGluIGNsdXN0ZXIKCmttZWFuUmVzdWx0cyRzaXplCmttZWFuUmVzdWx0czIkc2l6ZQprbWVhblJlc3VsdHMzJHNpemUKYGBgCgpUaGUgMXN0IHJlc3VsdCBzaG93cyB0aGF0IGluIHRoZSAyLW1lYW5zIGNsdXN0ZXIsIHRoZXJlIGFyZSAyIGNsdXN0ZXJzIHdpdGggcmVzcGVjdGl2ZSBudW1iZXIgb2YgaXRlbXM6IDIxOSBhbmQgNTA4LiBUaGUgMm5kIHJlc3VsdCBzaG93cyB0aGF0IGluIHRoZSAzLW1lYW5zIGNsdXN0ZXIsIHRoZXJlIGFyZSAzIGNsdXN0ZXJzIHdpdGggcmVzcGVjdGl2ZSBudW1iZXIgb2YgaXRlbXM6IDE5MCwgMzIxIGFuZCAyMTYuIFRoZSAzcmQgcmVzdWx0IHNob3dzIHRoYXQgaW4gdGhlIDQtbWVhbnMgY2x1c3RlciwgdGhlcmUgYXJlIDQgY2x1c3RlcnMgd2l0aCByZXNwZWN0aXZlIG51bWJlciBvZiBpdGVtczogMTEzLCAxNjYsIDIwMCBhbmQgMjQ4LgoKSW4gdGhlIGNvZGUgYmVsb3csIHdlIHN0YXJ0ZWQgdGhlIHN0ZXBzIG9mIGNhbGN1bGF0aW5nIHRoZSBCQ3ViZWQgUHJlY2lzaW9uIGZpcnN0IGJhc2VkIG9uIGFjcXVpcmVkIGl0ZW1zIHdoaWNoIGFyZSBlbmNvZGVkIGFzIHN0YXR1cyA9JzEnLiBBcyBleHBsYWluZWQgbm90IHRvbyBsb25nIGJlZm9yZSwgdGhlIEJDdWJlZCBQcmVjaXNpb24gaXMgYSBtZWFzdXJlIG9mIHRoZSBwdXJpdHkgb2YgYSBjbHVzdGVyLiBQdXJpdHkgbWVhbnMgaW4gb3VyIGNhc2UsIHRoZSBtb3JlICdhY3F1aXJlZCcgY2xhc3MgbGFiZWxzIGFyZSBncm91cGVkIHRvZ2V0aGVyLCB0aGUgcHVyZXIgdGhhdCBjbHVzdGVyIHdpbGwgYmUuIElmIGEgY2x1c3RlciBvbmx5IGNvbnNpc3RzIG9mIGFjcXVpcmVkIGl0ZW1zLCBpdCBtZWFucyB0aGUgY2x1c3RlciBoYXMgMTAwJSBwcmVjaXNpb24gb3IgaXMgMTAwJSBwdXJlLiBCQ3ViZWQgUHJlY2lzaW9uIGlzIGNhbGN1bGF0ZWQgYXMgbnVtYmVyIG9mIGFjcXVpcmVkIGl0ZW1zIGRpdmlkZWQgYnkgdGhlIHRvdGFsIG51bWJlciBvZiBpdGVtcyBpbiB0aGUgY2x1c3Rlci4KCmBgYHtyfQojMy0gQ2FsY3VsYXRlIHRoZSBCQ3ViZWQgUHJlY2lzaW9uCiNCQ3ViZWRQcmVjaXNpb24gPSBOdW1iZXJPZkFjcXVpcmVkSXRlbXMvVG90YWxOdW1iZXJPZkl0ZW1zSW5DbHVzdGVyCgojMy4xOiBQcmVjaXNpb24gb2YgYWNxdWlyZWQgaW4gMi1tZWFuIGNsdXN0ZXJzIGFmdGVyIGZpbmRpbmcgb3V0IGhvdyBtYW55IGFjcXVpcmVkIGl0ZW1zIGFyZSBpbiBlYWNoIGNsdXN0ZXIKClRvdGFsTnVtYmVyT2ZJdGVtc0luQ2x1c3RlcjE9MjE5ICNUb3RhbCBOdW1iZXIgb2YgSXRlbXMgaW4gQ2x1c3RlciAxIGFzIGZvdW5kIGFuZCBleHBsYWluZWQgaW4gdGhlIHByZXZpb3VzIGNvZGUgc25pcHBldC4KIyBUaGlzIGNvZGUgZG93biBiZWxvdyBjYWxjdWxhdGVzIHRoZSBudW1iZXIgb2YgYWNxdWlyZWQgaXRlbXMgKGVuY29kZWQgYXMgJzEnKSBpbiBDbHVzdGVyIDEgb2YgMi1tZWFucyBjbHVzdGVyIAphY3F1aXJlZDJDbHVzdDE8LXByaW50KHN1bShwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcyRzdGF0dXNba21lYW5SZXN1bHRzJGNsdXN0ZXIgPT0iMSJdPT0gIjEiLCBuYS5ybSA9IFRSVUUpKQojUmVzdWx0IG9mIEJDdWJlZCBQcmVjaXNpb24KcHJpbnQoYWNxdWlyZWQyQ2x1c3QxKjEwMC9Ub3RhbE51bWJlck9mSXRlbXNJbkNsdXN0ZXIxKQoKVG90YWxOdW1iZXJPZkl0ZW1zSW5DbHVzdGVyPTUwOCAjVG90YWwgTnVtYmVyIG9mIEl0ZW1zIGluIENsdXN0ZXIgMiBhcyBmb3VuZCBhbmQgZXhwbGFpbmVkIGluIHRoZSBwcmV2aW91cyBjb2RlIHNuaXBwZXQuCiMgVGhpcyBjb2RlIGRvd24gYmVsb3cgY2FsY3VsYXRlcyB0aGUgbnVtYmVyIG9mIGFjcXVpcmVkIGl0ZW1zIChlbmNvZGVkIGFzICcxJykgaW4gQ2x1c3RlciAyIG9mIDItbWVhbnMgY2x1c3RlcgphY3F1aXJlZDJDbHVzdDI8LXByaW50KHN1bShwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcyRzdGF0dXNba21lYW5SZXN1bHRzJGNsdXN0ZXIgPT0iMiJdPT0gIjEiLCBuYS5ybSA9IFRSVUUpKQojUmVzdWx0IG9mIEJDdWJlZCBQcmVjaXNpb24KcHJpbnQoYWNxdWlyZWQyQ2x1c3QyKjEwMC9Ub3RhbE51bWJlck9mSXRlbXNJbkNsdXN0ZXIpCmBgYAoKIyMjIFJlc3VsdHMgb2YgMi1tZWFucyBjbHVzdGVyaW5nIEJDdWJlZCBQcmVjaXNpb24gZm9yIDIgY2x1c3RlcnMKCnwgQ2x1c3RlciBObyAgICB8IE5vIE9mIEl0ZW1zIHwgTm8gT2YgQWNxdWlyZWQgSXRlbXMgfCBCQ3ViZWQgUHJlY2lzaW9uIHwKfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tfAp8ICoqQ2x1c3RlciAxKiogfCAyMTkgICAgICAgICB8IDE4NiAgICAgICAgICAgICAgICAgIHwgODQuOTMxNTElICAgICAgICB8CnwgKipDbHVzdGVyIDIqKiB8IDUwOCAgICAgICAgIHwgMjY2ICAgICAgICAgICAgICAgICAgfCA1Mi4zNjIyJSAgICAgICAgIHwKCkNsdXN0ZXIgMSBpcyBtb3N0bHkgcHVyZSBhcyBpdHMgcHJlY2lzaW9uIGlzIGhpZ2ggd2hpbGUgQ2x1c3RlciAyIGlzIG5vdCBwdXJlIGFzIGl0cyBwcmVjaXNpb24gaXMgY2xvc2UgdG8gaGFsZiB3aGljaCBtZWFucyBpdCBpcyBhbG1vc3QgZXF1YWxseSBtaXhlZCB3aXRoIGFjcXVpcmVkIGl0ZW1zIGFuZCBjbG9zZWQgaXRlbXMuCgpgYGB7cn0KIzMuMTogUHJlY2lzaW9uIG9mIGFjcXVpcmVkIGluIDMtbWVhbiBjbHVzdGVycyBhZnRlciBmaW5kaW5nIG91dCBob3cgbWFueSBhY3F1aXJlZCBpdGVtcyBhcmUgaW4gZWFjaCBjbHVzdGVyCgoKVG90YWxOdW1iZXJPZkl0ZW1zSW5DbHVzdGVyPTE5MCAjVG90YWwgTm8gb2YgSXRlbXMgaW4gQ2x1c3RlciAxIGFzIGZvdW5kIGFuZCBleHBsYWluZWQgaW4gdGhlIGNvZGUgc25pcHBldCBiZWZvcmUgQkN1YmVkLgojIFRoaXMgY29kZSBkb3duIGJlbG93IGNhbGN1bGF0ZXMgdGhlIG51bWJlciBvZiBhY3F1aXJlZCBpdGVtcyAoZW5jb2RlZCBhcyAnMScpIGluIENsdXN0ZXIgMSBvZiAzLW1lYW5zIGNsdXN0ZXIKYWNxdWlyZWQzQ2x1c3QxPC1wcmludChzdW0ocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMkc3RhdHVzW2ttZWFuUmVzdWx0czIkY2x1c3RlciA9PSIxIl09PSAiMSIsIG5hLnJtID0gVFJVRSkpCiNSZXN1bHQgb2YgQkN1YmVkIFByZWNpc2lvbgpwcmludChhY3F1aXJlZDNDbHVzdDEqMTAwL1RvdGFsTnVtYmVyT2ZJdGVtc0luQ2x1c3RlcikKClRvdGFsTnVtYmVyT2ZJdGVtc0luQ2x1c3Rlcj0zMjEgI1RvdGFsIE5vIG9mIEl0ZW1zIGluIENsdXN0ZXIgMiBhcyBmb3VuZCBhbmQgZXhwbGFpbmVkIGluIHRoZSBjb2RlIHNuaXBwZXQgYmVmb3JlIEJDdWJlZC4KIyBUaGlzIGNvZGUgZG93biBiZWxvdyBjYWxjdWxhdGVzIHRoZSBudW1iZXIgb2YgYWNxdWlyZWQgaXRlbXMgKGVuY29kZWQgYXMgJzEnKSBpbiBDbHVzdGVyIDIgb2YgMy1tZWFucyBjbHVzdGVyCmFjcXVpcmVkM0NsdXN0MjwtcHJpbnQoc3VtKHByZXByb2Nlc3NlZF9kYXRhc2V0LmZlYXR1cmVzJHN0YXR1c1trbWVhblJlc3VsdHMyJGNsdXN0ZXIgPT0iMiJdPT0gIjEiLCBuYS5ybSA9IFRSVUUpKQojUmVzdWx0IG9mIEJDdWJlZCBQcmVjaXNpb24KcHJpbnQoYWNxdWlyZWQzQ2x1c3QyKjEwMC9Ub3RhbE51bWJlck9mSXRlbXNJbkNsdXN0ZXIpCgpUb3RhbE51bWJlck9mSXRlbXNJbkNsdXN0ZXI9MjE2ICNUb3RhbCBObyBvZiBJdGVtcyBpbiBDbHVzdGVyIDMgYXMgZm91bmQgYW5kIGV4cGxhaW5lZCBpbiB0aGUgY29kZSBzbmlwcGV0IGJlZm9yZSBCQ3ViZWQuCiMgVGhpcyBjb2RlIGRvd24gYmVsb3cgY2FsY3VsYXRlcyB0aGUgbnVtYmVyIG9mIGFjcXVpcmVkIGl0ZW1zIChlbmNvZGVkIGFzICcxJykgaW4gQ2x1c3RlciAzIG9mIDMtbWVhbnMgY2x1c3RlcgphY3F1aXJlZDNDbHVzdDM8LXByaW50KHN1bShwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcyRzdGF0dXNba21lYW5SZXN1bHRzMiRjbHVzdGVyID09IjMiXT09ICIxIiwgbmEucm0gPSBUUlVFKSkKI1Jlc3VsdCBvZiBCQ3ViZWQgUHJlY2lzaW9uCnByaW50KGFjcXVpcmVkM0NsdXN0MyoxMDAvVG90YWxOdW1iZXJPZkl0ZW1zSW5DbHVzdGVyKQoKYGBgCgojIyMgUmVzdWx0cyBvZiAzLW1lYW5zIGNsdXN0ZXJpbmcgQkN1YmVkIFByZWNpc2lvbiBmb3IgMyBjbHVzdGVycwoKfCBDbHVzdGVyIE5vICAgIHwgTm8gT2YgSXRlbXMgfCBObyBPZiBBY3F1aXJlZCBJdGVtcyB8IEJDdWJlZCBQcmVjaXNpb24gfAp8LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS18CnwgKipDbHVzdGVyIDEqKiB8IDE5MCAgICAgICAgIHwgMTY0ICAgICAgICAgICAgICAgICAgfCA4Ni4zMTU3OSUgICAgICAgIHwKfCAqKkNsdXN0ZXIgMioqIHwgMzIxICAgICAgICAgfCAxNTYgICAgICAgICAgICAgICAgICB8IDQ4LjU5ODEzJSAgICAgICAgfAp8ICoqQ2x1c3RlciAzKiogfCAyMTYgICAgICAgICB8IDEzMiAgICAgICAgICAgICAgICAgIHwgNjEuMTExMTElICAgICAgICB8CgpDbHVzdGVyIDEgaXMgbW9zdGx5IHB1cmUgYXMgcHJlY2lzaW9uIGlzIGhpZ2guIENsdXN0ZXIgMiBpcyBub3QgcHVyZSBhdCBhbGwgYXMgaXQgaXMgYWxtb3N0IGVxdWFsbHkgbWl4ZWQgd2l0aCBhY3F1aXJlZCBhbmQgY2xvc2VkIGl0ZW1zLiBDbHVzdGVyIDMgaXMgYSBsaXR0bGUgYml0IHB1cmUgYXMgaXQgaXMgbW9yZSBhY3F1aXJlZCBpdGVtcyBidXQgbm90IGJ5IGEgaGlnaCBwZXJjZW50YWdlIG9mIHByZWNpc2lvbi4KCmBgYHtyfQojMy4xOiBQcmVjaXNpb24gb2YgYWNxdWlyZWQgaW4gNC1tZWFuIGNsdXN0ZXJzIGFmdGVyIGZpbmRpbmcgb3V0IGhvdyBtYW55IGFjcXVpcmVkIGl0ZW1zIGFyZSBpbiBlYWNoIGNsdXN0ZXIKClRvdGFsTnVtYmVyT2ZJdGVtc0luQ2x1c3Rlcj0xMTMgI1RvdGFsIE5vIG9mIEl0ZW1zIGluIENsdXN0ZXIgMSBhcyBmb3VuZCBhbmQgZXhwbGFpbmVkIGluIHRoZSBjb2RlIHNuaXBwZXQgYmVmb3JlIEJDdWJlZC4KIyBUaGlzIGNvZGUgZG93biBiZWxvdyBjYWxjdWxhdGVzIHRoZSBudW1iZXIgb2YgYWNxdWlyZWQgaXRlbXMgKGVuY29kZWQgYXMgJzEnKSBpbiBDbHVzdGVyIDEgb2YgNC1tZWFucyBjbHVzdGVyCmFjcXVpcmVkNENsdXN0MTwtcHJpbnQoc3VtKHByZXByb2Nlc3NlZF9kYXRhc2V0LmZlYXR1cmVzJHN0YXR1c1trbWVhblJlc3VsdHMzJGNsdXN0ZXIgPT0iMSJdPT0gIjEiLCBuYS5ybSA9IFRSVUUpKQojUmVzdWx0IG9mIEJDdWJlZCBQcmVjaXNpb24KcHJpbnQoYWNxdWlyZWQ0Q2x1c3QxKjEwMC9Ub3RhbE51bWJlck9mSXRlbXNJbkNsdXN0ZXIpCgpUb3RhbE51bWJlck9mSXRlbXNJbkNsdXN0ZXI9MTY2ICNUb3RhbCBObyBvZiBJdGVtcyBpbiBDbHVzdGVyIDIgYXMgZm91bmQgYW5kIGV4cGxhaW5lZCBpbiB0aGUgY29kZSBzbmlwcGV0IGJlZm9yZSBCQ3ViZWQuCiMgVGhpcyBjb2RlIGRvd24gYmVsb3cgY2FsY3VsYXRlcyB0aGUgbnVtYmVyIG9mIGFjcXVpcmVkIGl0ZW1zIChlbmNvZGVkIGFzICcxJykgaW4gQ2x1c3RlciAyIG9mIDQtbWVhbnMgY2x1c3RlcgphY3F1aXJlZDRDbHVzdDI8LXByaW50KHN1bShwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcyRzdGF0dXNba21lYW5SZXN1bHRzMyRjbHVzdGVyID09IjIiXT09ICIxIiwgbmEucm0gPSBUUlVFKSkKI1Jlc3VsdCBvZiBCQ3ViZWQgUHJlY2lzaW9uCnByaW50KGFjcXVpcmVkNENsdXN0MioxMDAvVG90YWxOdW1iZXJPZkl0ZW1zSW5DbHVzdGVyKQoKVG90YWxOdW1iZXJPZkl0ZW1zSW5DbHVzdGVyPTIwMCAjVG90YWwgTm8gb2YgSXRlbXMgaW4gQ2x1c3RlciAzIGFzIGZvdW5kIGFuZCBleHBsYWluZWQgaW4gdGhlIGNvZGUgc25pcHBldCBiZWZvcmUgQkN1YmVkLgojIFRoaXMgY29kZSBkb3duIGJlbG93IGNhbGN1bGF0ZXMgdGhlIG51bWJlciBvZiBhY3F1aXJlZCBpdGVtcyAoZW5jb2RlZCBhcyAnMScpIGluIENsdXN0ZXIgMyBvZiA0LW1lYW5zIGNsdXN0ZXIKYWNxdWlyZWQ0Q2x1c3QzPC1wcmludChzdW0ocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMkc3RhdHVzW2ttZWFuUmVzdWx0czMkY2x1c3RlciA9PSIzIl09PSAiMSIsIG5hLnJtID0gVFJVRSkpCiNSZXN1bHQgb2YgQkN1YmVkIFByZWNpc2lvbgpwcmludChhY3F1aXJlZDRDbHVzdDMqMTAwL1RvdGFsTnVtYmVyT2ZJdGVtc0luQ2x1c3RlcikKClRvdGFsTnVtYmVyT2ZJdGVtc0luQ2x1c3Rlcj0yNDggI1RvdGFsIE5vIG9mIEl0ZW1zIGluIENsdXN0ZXIgNCBhcyBmb3VuZCBhbmQgZXhwbGFpbmVkIGluIHRoZSBjb2RlIHNuaXBwZXQgYmVmb3JlIEJDdWJlZC4KIyBUaGlzIGNvZGUgZG93biBiZWxvdyBjYWxjdWxhdGVzIHRoZSBudW1iZXIgb2YgYWNxdWlyZWQgaXRlbXMgKGVuY29kZWQgYXMgJzEnKSBpbiBDbHVzdGVyIDQgb2YgNC1tZWFucyBjbHVzdGVyCmFjcXVpcmVkNENsdXN0NDwtcHJpbnQoc3VtKHByZXByb2Nlc3NlZF9kYXRhc2V0LmZlYXR1cmVzJHN0YXR1c1trbWVhblJlc3VsdHMzJGNsdXN0ZXIgPT0iNCJdPT0gIjEiLCBuYS5ybSA9IFRSVUUpKQojUmVzdWx0IG9mIEJDdWJlZCBQcmVjaXNpb24KcHJpbnQoYWNxdWlyZWQ0Q2x1c3Q0KjEwMC9Ub3RhbE51bWJlck9mSXRlbXNJbkNsdXN0ZXIpCmBgYAoKIyMjIFJlc3VsdHMgb2YgNC1tZWFucyBjbHVzdGVyaW5nIEJDdWJlZCBQcmVjaXNpb24gZm9yIDQgY2x1c3RlcnMKCnwgQ2x1c3RlciBObyAgICB8IE5vIE9mIEl0ZW1zIHwgTm8gT2YgQWNxdWlyZWQgSXRlbXMgfCBCQ3ViZWQgUHJlY2lzaW9uIHwKfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tfAp8ICoqQ2x1c3RlciAxKiogfCAxMTMgICAgICAgICB8IDEwMSAgICAgICAgICAgICAgICAgIHwgODkuMzgwNTMlICAgICAgICB8CnwgKipDbHVzdGVyIDIqKiB8IDE2NiAgICAgICAgIHwgMTI1ICAgICAgICAgICAgICAgICAgfCA3NS4zMDEyMCUgICAgICAgIHwKfCAqKkNsdXN0ZXIgMyoqIHwgMjAwICAgICAgICAgfCAxMjQgICAgICAgICAgICAgICAgICB8IDYyLjAwMDAwJSAgICAgICAgfAp8ICoqQ2x1c3RlciA0KiogfCAyNDggICAgICAgICB8IDEwMiAgICAgICAgICAgICAgICAgIHwgNDEuMTI5MDMlICAgICAgICB8CgpDbHVzdGVyIDEgaXMgYWxtb3N0IHB1cmUgYXMgaXQgaGFzIGEgZ29vZCBhbW91bnQgb2YgcHJlY2lzaW9uLiBDbHVzdGVyIDIgaXMgZmFpcmx5IHB1cmUuIENsdXN0ZXIgMyBpcyBhIGxpdHRsZSBiaXQgcHVyZSBidXQgd2l0aCBhIGhpZ2ggcGVyY2VudGFnZSBvZiBwcmVjaXNpb24uIENsdXN0ZXIgNCBpcyBub3QgcHVyZSBhdCBhbGwuIFJhdGhlciB0aGUgcHJlY2lzaW9uIGluZGljYXRlcyB0aGF0IHRoZSBudW1iZXIgb2YgYWNxdWlyZWQgaXRlbXMgaXMgbG93ZXIgdGhhbiB0aGUgbnVtYmVyIG9mIGNsb3NlZCBpdGVtcyBpbiB0aGlzIGNsdXN0ZXIuCgpJbiB0aGUgY29kZSBiZWxvdywgd2Ugc3RhcnRlZCB0aGUgc3RlcHMgb2YgY2FsY3VsYXRpbmcgdGhlIEJDdWJlZCBSZWNhbGwgYWxzbyBiYXNlZCBvbiBhY3F1aXJlZCBpdGVtcyB3aGljaCBhcmUgZW5jb2RlZCBhcyBzdGF0dXMgPScxJy4gVGhlIEJDdWJlZCBSZWNhbGwgaW5kaWNhdGVzIGhvdyBnb29kIHRoZSBkYXRhIHBvaW50cyBvZiBzYW1lIHRydWUgY2xhc3MgYXJlIHB1dCBpbnRvIHNhbWUgY2x1c3Rlci4gSW4gb3VyIGNhc2UsIGl0IGlzIGhvdyBnb29kIHRoZSBhY3F1aXJlZCBkYXRhIHBvaW50cyBhcmUgcHV0IGludG8gdGhlIHNhbWUgY2x1c3RlciBhcyBvdGhlciBhY3F1aXJlZCBkYXRhIHBvaW50cy4gVGhlcmVmb3JlIHRoZSBiZXR0ZXIgdGhlIHJlY2FsbCBpcywgdGhlIG1vcmUgaXQgc2hvd3MgdGhhdCBhY3F1aXJlZCBkYXRhIHBvaW50cyBhcmUgcHV0IHRvZ2V0aGVyLiBXZSBjYWxjdWxhdGVkIEJDdWJlZCBSZWNhbGwgYXMgbnVtYmVyIG9mIGFjcXVpcmVkIGl0ZW1zIGluIHRoZSBjbHVzdGVyIGRpdmlkZWQgYnkgdG90YWwgbnVtYmVyIG9mIGFjcXVpcmVkIGl0ZW1zIGluIHRoZSB3aG9sZSBkYXRhc2V0LgoKYGBge3J9CiM0LSBDYWxjdWxhdGUgdGhlIEJDdWJlZCBSZWNhbGwKI0JDdWJlZFJlY2FsbCA9IE51bWJlck9mQWNxdWlyZWRJdGVtc0luQ2x1c3Rlci9Ub3RhbEFjcXVpcmVkSXRlbXNJbkRhdGFzZXQKCiM0LjEtIEZpbmQgdGhlIG51bWJlciBvZiBhbGwgdGhlIHJvd3Mgd2l0aCBhY3F1aXJlZCBpbiB0aGUgY2xhc3MgYnkgZmlsdGVyaW5nICcxJyBpbiB0aGUgZGF0YXNldC5mZWF0dXJlcyBhbmQgc2VlaW5nIGhvdyBtYW55IGVudHJpZXMuCiMgTm90ZSB0aGF0IHdlIGhhdmUgZW5jb2RlZCBjbGFzcyBsYWJlbCAnYWNxdWlyZWQnIHRvICcxJyBmb3IgZWFzeSBjaGFuZ2UuIApUb3RhbEFjcXVpcmVkSXRlbXNJbkRhdGFzZXQgPSBwcmludChzdW0ocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMkc3RhdHVzID09ICIxIiwgbmEucm0gPSBUUlVFKSkKI051bWJlciBvZiAnYWNxdWlyZWQnPSA0NTIgcm93cwoKIzQuMjogUmVjYWxsIG9mIGFjcXVpcmVkIGluIDItbWVhbiBjbHVzdGVycwoKIyBUaGlzIGNvZGUgZG93biBiZWxvdyBjYWxjdWxhdGVzIHRoZSBudW1iZXIgb2YgYWNxdWlyZWQgaXRlbXMgKGVuY29kZWQgYXMgJzEnKSBpbiBDbHVzdGVyIDEgb2YgMi1tZWFucyBjbHVzdGVyIAphY3F1aXJlZDJDbHVzdDE8LXByaW50KHN1bShwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcyRzdGF0dXNba21lYW5SZXN1bHRzJGNsdXN0ZXIgPT0iMSJdPT0gIjEiLCBuYS5ybSA9IFRSVUUpKQpSZWNhbGwxQ2x1c3QxPSBwcmludChhY3F1aXJlZDJDbHVzdDEqMTAwL1RvdGFsQWNxdWlyZWRJdGVtc0luRGF0YXNldCkgCgojIFRoaXMgY29kZSBkb3duIGJlbG93IGNhbGN1bGF0ZXMgdGhlIG51bWJlciBvZiBhY3F1aXJlZCBpdGVtcyAoZW5jb2RlZCBhcyAnMScpIGluIENsdXN0ZXIgMiBvZiAyLW1lYW5zIGNsdXN0ZXIKYWNxdWlyZWQyQ2x1c3QyPC1wcmludChzdW0ocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMkc3RhdHVzW2ttZWFuUmVzdWx0cyRjbHVzdGVyID09IjIiXT09ICIxIiwgbmEucm0gPSBUUlVFKSkKUmVjYWxsMUNsdXN0Mj0gcHJpbnQoYWNxdWlyZWQyQ2x1c3QyKjEwMC9Ub3RhbEFjcXVpcmVkSXRlbXNJbkRhdGFzZXQpCmBgYAoKTm90ZSB0aGF0IHRoZSBmaXJzdCByZXN1bHQgaXMgdGhlIHRvdGFsIG51bWJlciBvZiBhY3F1aXJlZCBpdGVtcyBpbiB0aGUgd2hvbGUgZGF0YXNldC4gXCMjIyBSZXN1bHRzIG9mIDItbWVhbnMgY2x1c3RlcmluZyBCQ3ViZWQgUmVjYWxsIGZvciAyIGNsdXN0ZXIKCnwgQ2x1c3RlciBObyAgICB8IE5vIG9mIGFjcXVpcmVkIEl0ZW1zIHwgQkN1YmVkIFJlY2FsbCB8CnwtLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS18CnwgKipDbHVzdGVyIDEqKiB8IDE4NiAgICAgICAgICAgICAgICAgIHwgNDEuMTUwNDQlICAgICB8CnwgKipDbHVzdGVyIDIqKiB8IDI2NiAgICAgICAgICAgICAgICAgIHwgNTguODQ5NTklICAgICB8CgpGb3IgQ2x1c3RlciAxLCB0aGUgQkN1YmVkIFJlY2FsbCBpcyBsb3cgd2hpY2ggbWVhbnMgb25seSBsZXNzIHRoYW4gaGFsZiBvZiB0aGUgYWNxdWlyZWQgaXRlbXMgYXJlIHRvZ2V0aGVyIGluIHRoaXMgY2x1c3Rlci4gSW4gQ2x1c3RlciAyLCB0aGUgQkN1YmVkIFJlY2FsbCBpcyBhbHNvIGxvdyBob3dldmVyIGEgbGl0dGxlIG1vcmUgdGhhbiBoYWxmIG9mIHRoZSBhY3F1aXJlZCBpdGVtcyBhcmUgaW4gdGhpcyBjbHVzdGVyLgoKYGBge3J9CiNCQ3ViZWRSZWNhbGwgPSBOdW1iZXJPZkFjcXVpcmVkSXRlbXNJbkNsdXN0ZXIvVG90YWxBY3F1aXJlZEl0ZW1zSW5EYXRhc2V0CgojNC4xLSBGaW5kIHRoZSBudW1iZXIgb2YgYWxsIHRoZSByb3dzIHdpdGggYWNxdWlyZWQgaW4gdGhlIGNsYXNzIGJ5IGZpbHRlcmluZyAnMScgaW4gdGhlIGRhdGFzZXQuZmVhdHVyZXMgYW5kIHNlZWluZyBob3cgbWFueSBlbnRyaWVzLgojIE5vdGUgdGhhdCB3ZSBoYXZlIGVuY29kZWQgY2xhc3MgbGFiZWwgJ2FjcXVpcmVkJyB0byAnMScgZm9yIGVhc3kgY2hhbmdlLiAKVG90YWxBY3F1aXJlZEl0ZW1zSW5EYXRhc2V0ID0gcHJpbnQoc3VtKHByZXByb2Nlc3NlZF9kYXRhc2V0LmZlYXR1cmVzJHN0YXR1cyA9PSAiMSIsIG5hLnJtID0gVFJVRSkpCiNOdW1iZXIgb2YgJ2FjcXVpcmVkJz0gNDUyIHJvd3MKCiM0LjM6IFJlY2FsbCBvZiBhY3F1aXJlZCBpbiAzLW1lYW4gY2x1c3RlcnMgCgojIFRoaXMgY29kZSBkb3duIGJlbG93IGNhbGN1bGF0ZXMgdGhlIG51bWJlciBvZiBhY3F1aXJlZCBpdGVtcyAoZW5jb2RlZCBhcyAnMScpIGluIENsdXN0ZXIgMSBvZiAzLW1lYW5zIGNsdXN0ZXIKYWNxdWlyZWQzQ2x1c3QxPC1wcmludChzdW0ocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMkc3RhdHVzW2ttZWFuUmVzdWx0czIkY2x1c3RlciA9PSIxIl09PSAiMSIsIG5hLnJtID0gVFJVRSkpClJlY2FsbDNDbHVzdDE9IHByaW50KGFjcXVpcmVkM0NsdXN0MSoxMDAvVG90YWxBY3F1aXJlZEl0ZW1zSW5EYXRhc2V0KSAKCiMgVGhpcyBjb2RlIGRvd24gYmVsb3cgY2FsY3VsYXRlcyB0aGUgbnVtYmVyIG9mIGFjcXVpcmVkIGl0ZW1zIChlbmNvZGVkIGFzICcxJykgaW4gQ2x1c3RlciAyIG9mIDMtbWVhbnMgY2x1c3RlcgphY3F1aXJlZDNDbHVzdDI8LXByaW50KHN1bShwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcyRzdGF0dXNba21lYW5SZXN1bHRzMiRjbHVzdGVyID09IjIiXT09ICIxIiwgbmEucm0gPSBUUlVFKSkKUmVjYWxsM0NsdXN0Mj0gcHJpbnQoYWNxdWlyZWQzQ2x1c3QyKjEwMC9Ub3RhbEFjcXVpcmVkSXRlbXNJbkRhdGFzZXQpCgojIFRoaXMgY29kZSBkb3duIGJlbG93IGNhbGN1bGF0ZXMgdGhlIG51bWJlciBvZiBhY3F1aXJlZCBpdGVtcyAoZW5jb2RlZCBhcyAnMScpIGluIENsdXN0ZXIgMyBvZiAzLW1lYW5zIGNsdXN0ZXIKYWNxdWlyZWQzQ2x1c3QzPC1wcmludChzdW0ocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMkc3RhdHVzW2ttZWFuUmVzdWx0czIkY2x1c3RlciA9PSIzIl09PSAiMSIsIG5hLnJtID0gVFJVRSkpClJlY2FsbDNDbHVzdDM9IHByaW50KGFjcXVpcmVkM0NsdXN0MyoxMDAvVG90YWxBY3F1aXJlZEl0ZW1zSW5EYXRhc2V0KQpgYGAKCk5vdGUgdGhhdCBUb3RhbEFjcXVpcmVkSXRlbXNJbkRhdGFzZXQgaXMgdGhlIHRvdGFsIG51bWJlciBvZiBhY3F1aXJlZCBpdGVtcyBpbiB0aGUgd2hvbGUgZGF0YXNldC4KCiMjIyBSZXN1bHRzIG9mIDMtbWVhbnMgY2x1c3RlcmluZyBCQ3ViZWQgUmVjYWxsIGZvciAzIGNsdXN0ZXIKCnwgQ2x1c3RlciBObyAgICB8IE5vIG9mIGFjcXVpcmVkIEl0ZW1zIHwgQkN1YmVkIFJlY2FsbCB8CnwtLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS18CnwgKipDbHVzdGVyIDEqKiB8IDE2NCAgICAgICAgICAgICAgICAgIHwgMzYuMjgzMTklICAgICB8CnwgKipDbHVzdGVyIDIqKiB8IDE1NiAgICAgICAgICAgICAgICAgIHwgMzQuNTEzMjclICAgICB8CnwgKipDbHVzdGVyIDMqKiB8IDEzMiAgICAgICAgICAgICAgICAgIHwgMjkuMjAzNTQlICAgICB8CgpJbiBDbHVzdGVyIDEsIDIgYW5kIDMsIHRoZSBCQ3ViZWQgUmVjYWxsIGlzIHZlcnkgbG93IHdoaWNoIG1lYW5zIHRoZSBhY3F1aXJlZCBpdGVtcyBhcmUgbm90IHdlbGwgcHV0IHRvZ2V0aGVyIGluIGFueSBvZiB0aGUgY2x1c3RlcnMuVGhpcyBtYXkgYWxzbyBpbmRpY2F0ZSB0aGF0IDMtbWVhbiBjbHVzdGVyaW5nIGRvZXMgbm90IGhhdmUgYSBnb29kIHF1YWxpdHkuCgpgYGB7cn0KI0JDdWJlZFJlY2FsbCA9IE51bWJlck9mQWNxdWlyZWRJdGVtc0luQ2x1c3Rlci9Ub3RhbEFjcXVpcmVkSXRlbXNJbkRhdGFzZXQKCiM0LjEtIEZpbmQgdGhlIG51bWJlciBvZiBhbGwgdGhlIHJvd3Mgd2l0aCBhY3F1aXJlZCBpbiB0aGUgY2xhc3MgYnkgZmlsdGVyaW5nICcxJyBpbiB0aGUgZGF0YXNldC5mZWF0dXJlcyBhbmQgc2VlaW5nIGhvdyBtYW55IGVudHJpZXMuCiMgTm90ZSB0aGF0IHdlIGhhdmUgZW5jb2RlZCBjbGFzcyBsYWJlbCAnYWNxdWlyZWQnIHRvICcxJyBmb3IgZWFzeSBjaGFuZ2UuIApUb3RhbEFjcXVpcmVkSXRlbXNJbkRhdGFzZXQgPSBwcmludChzdW0ocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMkc3RhdHVzID09ICIxIiwgbmEucm0gPSBUUlVFKSkKI051bWJlciBvZiAnYWNxdWlyZWQnPSA0NTIgcm93cwoKIzQuNDogUmVjYWxsIG9mIGFjcXVpcmVkIGluIDQtbWVhbiBjbHVzdGVycyAKCiMgVGhpcyBjb2RlIGRvd24gYmVsb3cgY2FsY3VsYXRlcyB0aGUgbnVtYmVyIG9mIGFjcXVpcmVkIGl0ZW1zIChlbmNvZGVkIGFzICcxJykgaW4gQ2x1c3RlciAxIG9mIDQtbWVhbnMgY2x1c3RlcgphY3F1aXJlZDRDbHVzdDE8LXByaW50KHN1bShwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcyRzdGF0dXNba21lYW5SZXN1bHRzMyRjbHVzdGVyID09IjEiXT09ICIxIiwgbmEucm0gPSBUUlVFKSkKUmVjYWxsNENsdXN0MT0gcHJpbnQoYWNxdWlyZWQ0Q2x1c3QxKjEwMC9Ub3RhbEFjcXVpcmVkSXRlbXNJbkRhdGFzZXQpIAoKIyBUaGlzIGNvZGUgZG93biBiZWxvdyBjYWxjdWxhdGVzIHRoZSBudW1iZXIgb2YgYWNxdWlyZWQgaXRlbXMgKGVuY29kZWQgYXMgJzEnKSBpbiBDbHVzdGVyIDIgb2YgNC1tZWFucyBjbHVzdGVyCmFjcXVpcmVkNENsdXN0MjwtcHJpbnQoc3VtKHByZXByb2Nlc3NlZF9kYXRhc2V0LmZlYXR1cmVzJHN0YXR1c1trbWVhblJlc3VsdHMzJGNsdXN0ZXIgPT0iMiJdPT0gIjEiLCBuYS5ybSA9IFRSVUUpKQpSZWNhbGw0Q2x1c3QyPSBwcmludChhY3F1aXJlZDRDbHVzdDIqMTAwL1RvdGFsQWNxdWlyZWRJdGVtc0luRGF0YXNldCkKCiMgVGhpcyBjb2RlIGRvd24gYmVsb3cgY2FsY3VsYXRlcyB0aGUgbnVtYmVyIG9mIGFjcXVpcmVkIGl0ZW1zIChlbmNvZGVkIGFzICcxJykgaW4gQ2x1c3RlciAzIG9mIDQtbWVhbnMgY2x1c3RlcgphY3F1aXJlZDRDbHVzdDM8LXByaW50KHN1bShwcmVwcm9jZXNzZWRfZGF0YXNldC5mZWF0dXJlcyRzdGF0dXNba21lYW5SZXN1bHRzMyRjbHVzdGVyID09IjMiXT09ICIxIiwgbmEucm0gPSBUUlVFKSkKUmVjYWxsNENsdXN0Mz0gcHJpbnQoYWNxdWlyZWQ0Q2x1c3QzKjEwMC9Ub3RhbEFjcXVpcmVkSXRlbXNJbkRhdGFzZXQpCgojIFRoaXMgY29kZSBkb3duIGJlbG93IGNhbGN1bGF0ZXMgdGhlIG51bWJlciBvZiBhY3F1aXJlZCBpdGVtcyAoZW5jb2RlZCBhcyAnMScpIGluIENsdXN0ZXIgNCBvZiA0LW1lYW5zIGNsdXN0ZXIKYWNxdWlyZWQ0Q2x1c3Q0PC1wcmludChzdW0ocHJlcHJvY2Vzc2VkX2RhdGFzZXQuZmVhdHVyZXMkc3RhdHVzW2ttZWFuUmVzdWx0czMkY2x1c3RlciA9PSI0Il09PSAiMSIsIG5hLnJtID0gVFJVRSkpClJlY2FsbDRDbHVzdDQ9IHByaW50KGFjcXVpcmVkNENsdXN0NCoxMDAvVG90YWxBY3F1aXJlZEl0ZW1zSW5EYXRhc2V0KQpgYGAKCk5vdGUgdGhhdCBUb3RhbEFjcXVpcmVkSXRlbXNJbkRhdGFzZXQgaXMgdGhlIHRvdGFsIG51bWJlciBvZiBhY3F1aXJlZCBpdGVtcyBpbiB0aGUgd2hvbGUgZGF0YXNldC4gUmVzdWx0cyBvZiA0LW1lYW5zIGNsdXN0ZXJpbmcgQkN1YmVkIFJlY2FsbCBmb3IgNCBjbHVzdGVyCgp8IENsdXN0ZXIgTm8gICAgfCBObyBvZiBhY3F1aXJlZCBJdGVtcyB8IEJDdWJlZCBSZWNhbGwgfAp8LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tfAp8ICoqQ2x1c3RlciAxKiogfCAxMDEgICAgICAgICAgICAgICAgICB8IDIyLjM0NTEzJSAgICAgfAp8ICoqQ2x1c3RlciAyKiogfCAxMjUgICAgICAgICAgICAgICAgICB8IDI3LjY1NDg3JSAgICAgfAp8ICoqQ2x1c3RlciAzKiogfCAxMjQgICAgICAgICAgICAgICAgICB8IDI3LjQzMzYzJSAgICAgfAp8ICoqQ2x1c3RlciA0KiogfCAxMDIgICAgICAgICAgICAgICAgICB8IDIyLjU2NjM3JSAgICAgfAoKSW4gQ2x1c3RlciAxLCAyLCAzIGFuZCA0LCB0aGUgQkN1YmVkIFJlY2FsbCBpcyB2ZXJ5IGxvdywgc28gbm9uZSBvZiB0aGUgYWNxdWlyZWQgaXRlbXMgYXJlIHdlbGwgcHV0IHRvZ2V0aGVyLiBUaGlzIG1heSBhbHNvIGluZGljYXRlIHRoYXQgNC1tZWFuIGNsdXN0ZXJpbmcgZG9lcyBub3QgaGF2ZSBhIGdvb2QgcXVhbGl0eS4KCiMgNi1FdmFsdWF0aW9uIGFuZCBDb21wYXJpc29uIAoKIyMgNi4xLUNsYXNzaWZpY2F0aW9uIAoKfCAgICAgICAgICAgICB8NzA6MzAgIHw3MDozMCAgICB8NzA6MzAgICAgICB8IDgwOjIwICB8IDgwOjIwICAgIHwgODA6MjAgICAgICB8IDkwOjEwICB8IDkwOjEwICAgIHwgOTA6MTAgICAgICB8CnwgLS0tLS0tLS0tLS0gfCAtLS0tLS0gfCAtLS0tLS0tLSB8IC0tLS0tLS0tLS0gfCAtLS0tLS0gfCAtLS0tLS0tLSB8IC0tLS0tLS0tLS0gfCAtLS0tLS0gfCAtLS0tLS0tLSB8IC0tLS0tLS0tLS0gfAp8ICAgICAgICAgICAgIHwgKipJRyoqICAgICB8ICoqSUcgUmF0aW8qKiB8ICoqR2luaSBJbmRleCoqIHwgKipJRyoqICAgICB8ICoqSUcgUmF0aW8qKiB8ICoqR2luaSBJbmRleCoqIHwgKipJRyoqICAgICB8ICoqSUcgUmF0aW8qKiB8ICoqR2luaSBJbmRleCoqIHwKfCBBY2N1cmFjeSAgICB8IDExLjIlICB8IDY3LjQ2JSAgIHwgICoqNzkuMjklKiogICAgIHwgMjYuMTclIHwgNzYuNjQlICAgfCA3My44MyUgICAgIHwgOS41MiUgIHwgNzYuMTklICAgfCA3Ny43OCUgICAgIHwKfCBQcmVjaXNpb24gICB8IDEwMCAlICB8IDY0JSAgICAgIHwgODQuNzUlICAgICB8IDg1LjE5JSB8IDczLjQ3JSAgIHwgNzEuNzQlICAgICB8IDg1LjcxJSB8IDY2LjY3JSAgIHwgNjkuMjMlICAgICB8CnwgU2Vuc2l0aXZpdHkgfCAzNC4yICUgfCA2My4xNiUgICB8IDY1Ljc5JSAgICAgfCA3NC4xOSUgfCA3NSUgICAgICB8IDY4Ljc1JSAgICAgfCA3NSUgICAgfCA4NC42MiUgICB8IDY5LjIzJSAgICAgfAp8IFNwZWNpZmljaXR5IHwgMTAwICUgIHwgNzAuOTclICAgfCA5MC4zMiUgICAgIHwgNTUuNTYlIHwgNzcuOTclICAgfCA3Ny45NyUgICAgIHwgMCUgICAgIHwgNzAuMjclICAgfCA4My43OCUgICAgIHwKCjEuCSoqSW5mb3JtYXRpb24gR2FpbiAoSUcpIGFuZCBHaW5pIEluZGV4OioqIEhpZ2hlciB2YWx1ZXMgZm9yIElHIGFuZCBsb3dlciB2YWx1ZXMgZm9yIHRoZSBHaW5pIEluZGV4IGFyZSBkZXNpcmFibGUuIEZvciA3MDozMCBzcGxpdCwgR2luaSBJbmRleCBoYXMgdGhlIGxvd2VzdCB2YWx1ZSAoNzkuMjklKSwgaW5kaWNhdGluZyBiZXR0ZXIgc2VwYXJhdGlvbi4KMi4JKipBY2N1cmFjeSwgUHJlY2lzaW9uLCBTZW5zaXRpdml0eSwgYW5kIFNwZWNpZmljaXR5OioqIEhpZ2hlciBhY2N1cmFjeSwgcHJlY2lzaW9uLCBzZW5zaXRpdml0eSwgYW5kIHNwZWNpZmljaXR5IGFyZSBkZXNpcmVkLiBGb3IgNzA6MzAgc3BsaXQsIEdpbmkgSW5kZXggYWNoaWV2ZXMgdGhlIGhpZ2hlc3QgYWNjdXJhY3kgKDc5LjI5JSksIHByZWNpc2lvbiAoODQuNzUlKSwgc2Vuc2l0aXZpdHkgKDY1Ljc5JSksIGFuZCBzcGVjaWZpY2l0eSAoOTAuMzIlKS4KCldlIGV2YWx1YXRlZCB0aGUgcGVyZm9ybWFuY2Ugb2YgSW5mb3JtYXRpb24gR2FpbiwgR2FpbiBSYXRpbywgYW5kIEdpbmkgSW5kZXggYWNyb3NzIHRocmVlIHBhcnRpdGlvbnMgKDcwOjMwLCA4MDoyMCwgYW5kIDkwOjEwKSBieSBjYWxjdWxhdGluZyBhY2N1cmFjeSwgcHJlY2lzaW9uLCBzZW5zaXRpdml0eSwgYW5kIHNwZWNpZmljaXR5LiBXaXRoIGEgYmFsYW5jZWQgZGF0YXNldCwgd2UgcmVsaWVkIG9uIGFjY3VyYWN5IGFzIHRoZSBwcmltYXJ5IG1ldHJpYyB0byBqdWRnZSBhbGdvcml0aG0gcGVyZm9ybWFuY2UuIFRoZSBHaW5pIEluZGV4IGZvciB0aGUgNzA6MzAgcGFydGl0aW9uIGVtZXJnZWQgYXMgdGhlIGJlc3QtcGVyZm9ybWluZyBhbGdvcml0aG0sIGVtcGhhc2l6aW5nIHRoYXQgbW9yZSBub2RlcyBkb27igJl0IG5lY2Vzc2FyaWx5IGxlYWQgdG8gYmV0dGVyIGFjY3VyYWN5LiBEZXNwaXRlIGhhdmluZyBvbmx5IDggbm9kZXMgYW5kIHRlc3RpbmcgdGhyZWUgZmVhdHVyZXMsIHRoZSBHaW5pIEluZGV4IGFjaGlldmVkIHN1cGVyaW9yIGFjY3VyYWN5LgoKQWNyb3NzIHRoZSB0aHJlZSBwYXJ0aXRpb25zLCBJbmZvcm1hdGlvbiBHYWluIHlpZWxkZWQgdGhlIGxvd2VzdCBhdmVyYWdlIGFjY3VyYWN5ICgxNS42MyUpLCB3aGlsZSBHYWluIFJhdGlvIHNob3dlZCBhbiBhdmVyYWdlIGFjY3VyYWN5IG9mIDczLjQzJS4gTm90YWJseSwgdGhlIEdpbmkgSW5kZXggb3V0cGVyZm9ybWVkIGJvdGgsIGJvYXN0aW5nIGFuIGF2ZXJhZ2UgYWNjdXJhY3kgb2YgNzYuOTclLiBJbiBhZGRpdGlvbiwgdGhlIDcwOjMwIHNwbGl0IHN0cnVjayBhIGJhbGFuY2UgdGhhdCByZXN1bHRlZCBpbiB0aGUgYmVzdCBhY2N1cmFjeSwgYXMgb2JzZXJ2ZWQgdGhyb3VnaCB0aGUgR2luaSBJbmRleCwgSW5mb3JtYXRpb24gR2FpbiwgYW5kIEdhaW4gUmF0aW8uIAoKKipDb25zaWRlcmluZyB0aGVzZSBmYWN0b3JzLCB0aGUgNzA6MzAgc3BsaXQgd2l0aCBHaW5pIEluZGV4IGlzIHRoZSBtb3N0IG9wdGltYWwgY2hvaWNlIGZvciBjbGFzc2lmaWNhdGlvbi4qKgoKCiMjIDYuMi1DbHVzdGVyaW5nIAoKfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEsgPSAyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEsgPSAzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEsgPSA0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwtLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS18CnwgKipBdmVyYWdlIFNpbGhvdWV0dGUgd2lkdGgqKiAgICAgICAgICAgfCAwLjMzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjI3ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjIyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8ICoqVG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZSoqIHwgMjA3NzkuODQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMTYxNzAuMzEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMTQ0NTguMjUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAqKkJDdWJlZCBwcmVjaXNpb24qKiAgICAgICAgICAgICAgICAgICB8IDY4LjYlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IDY1LjMlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IDY2LjklICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgKipCQ3ViZWQgcmVjYWxsKiogICAgICAgICAgICAgICAgICAgICAgfCA1MC4wJSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAzMy4zJSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAyNSUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8ICoqVmlzdWFsaXphdGlvbioqICAgICAgICAgICAgICAgICAgICAgIHwgIVtdKC9Vc2Vycy9iYXNtYWFsc3VsYWltL0Rlc2t0b3AvaW1hZ2VzLzItbWVhbnMucG5nKSB8ICFbXSgvVXNlcnMvYmFzbWFhbHN1bGFpbS9EZXNrdG9wL2ltYWdlcy8zLW1lYW5zLnBuZykgfCAhW10oL1VzZXJzL2Jhc21hYWxzdWxhaW0vRGVza3RvcC9pbWFnZXMvNC1tZWFucy5wbmcpIHwKCgoxLgkqKkF2ZXJhZ2UgU2lsaG91ZXR0ZSBXaWR0aDoqKiBIaWdoZXIgdmFsdWVzIGFyZSBiZXR0ZXIsIGluZGljYXRpbmcgYmV0dGVyLWRlZmluZWQgY2x1c3RlcnMuIFRoZXJlZm9yZSwgZm9yIEsgPSAyLCBpdCBoYXMgdGhlIGhpZ2hlc3QgdmFsdWUgKDAuMzMpLCBzdWdnZXN0aW5nIGJldHRlciBzZXBhcmF0aW9uLiAKMi4JKipUb3RhbCBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlOioqIExvd2VyIHZhbHVlcyBhcmUgYmV0dGVyLCBpbmRpY2F0aW5nIGNvbXBhY3QgY2x1c3RlcnMuIEsgPSA0IGhhcyB0aGUgbG93ZXN0IHZhbHVlICgxNDQ1OC4yNSkuIDMuCSoqQkN1YmVkIFByZWNpc2lvbiBhbmQgUmVjYWxsOioqIEhpZ2hlciBwcmVjaXNpb24gYW5kIHJlY2FsbCBhcmUgZGVzaXJhYmxlLiBMb29raW5nIGF0IEsgPSAyLCBpdCBoYXMgYSBnb29kIGJhbGFuY2UgYmV0d2VlbiBwcmVjaXNpb24gKDY4LjYlKSBhbmQgcmVjYWxsICg1MCUpLiAKCioqQ29uc2lkZXJpbmcgdGhlc2UgZmFjdG9ycywgMi1tZWFucyBzZWVtcyB0byBiZSB0aGUgbW9zdCBvcHRpbWFsIGNob2ljZSBmb3IgY2x1c3RlcmluZy4qKgoKCiMgNy1SZXN1bHRzIGFuZCBGaW5kaW5ncyAgCgpUbyBzdW1tYXJpemU6PGJyPiAKCioq4oCiCUJlc3QgQ2x1c3RlcmluZyBBbGdvcml0aG06IEstbWVhbnMgd2l0aCBLID0gMi4qKjxicj4KKirigKIJQmVzdCBDbGFzc2lmaWNhdGlvbiBBbGdvcml0aG06IERlY2lzaW9uIHRyZWUgd2l0aCBhIDcwOjMwIHNwbGl0IHVzaW5nIHRoZSBHaW5pIEluZGV4LioqIAoKRm9yIGEgc21hbGwgZGF0YXNldCB3aXRoIDcyNyByb3dzLCB0aGUgY2hvaWNlIGJldHdlZW4gdGhlIGNsdXN0ZXJpbmcgYWxnb3JpdGhtIChLLW1lYW5zIHdpdGggSyA9IDIpIGFuZCB0aGUgY2xhc3NpZmljYXRpb24gYWxnb3JpdGhtIChEZWNpc2lvbiB0cmVlIHdpdGggR2luaSBJbmRleCwgNzA6MzAgc3BsaXQpIGRlcGVuZHMgb24gc2V2ZXJhbCBmYWN0b3JzOiAKCjEuCSoqU2l6ZSBvZiB0aGUgRGF0YXNldDoqKiBLLW1lYW5zIGNhbiBiZSBjb21wdXRhdGlvbmFsbHkgZWZmaWNpZW50IGZvciBzdGFydHVwIGRhdGFzZXQgd2hpY2ggaXMgc21hbGwuIEhvd2V2ZXIsIGRlY2lzaW9uIHRyZWVzLCBlc3BlY2lhbGx5IHdpdGggYSBsaW1pdGVkIGRlcHRoLCBjYW4gYWxzbyBoYW5kbGUgc21hbGxlciBkYXRhc2V0cyBlZmZlY3RpdmVseS4gCgoyLgkqKk5hdHVyZSBvZiB0aGUgRGF0YToqKiBCZWNhdXNlIHRoZSBzdGFydHVwIGRhdGEgZG9lc24ndCBmb3JtIGRpc3RpbmN0IGNsdXN0ZXJzLCBLLW1lYW5zIGlzIG5vdCByZWNvbW1lbmRlZC4gSG93ZXZlciwgdGhlcmUgYXJlIGNsZWFyIHBhdHRlcm5zIGFuZCByZWxhdGlvbnNoaXBzIGJldHdlZW4gZmVhdHVyZXMgdGhhdCB3YXMgY2FwdHVyZWQgYnkgdGhlIDcwOjMwIGRlY2lzaW9uIHRyZWUuIAoKMy4JKipJbnRlcnByZXRhYmlsaXR5OioqIERlY2lzaW9uIHRyZWVzIGFyZSBvZnRlbiBtb3JlIGludGVycHJldGFibGUsIHByb3ZpZGluZyBpbnNpZ2h0cyBpbnRvIHRoZSBkZWNpc2lvbi1tYWtpbmcgcHJvY2Vzcywgd2hpY2ggY2FuIGJlIHZhbHVhYmxlIGluIHVuZGVyc3RhbmRpbmcgdGhlIGRhdGEuIAoKNC4JKipDb21wdXRhdGlvbmFsIFJlc291cmNlczoqKiBLLW1lYW5zIGlzIGdlbmVyYWxseSBjb21wdXRhdGlvbmFsbHkgZWZmaWNpZW50LCBidXQgdGhlIGRhdGFzZXQgc2l6ZSBpcyBzdGlsbCByZWxhdGl2ZWx5IHNtYWxsLiAKCkdpdmVuIHRoZXNlIGNvbnNpZGVyYXRpb25zLCBiZWNhdXNlIGludGVycHJldGFiaWxpdHkgYW5kIHVuZGVyc3RhbmRpbmcgdGhlIGRlY2lzaW9uIHByb2Nlc3MgYXJlIGltcG9ydGFudCwgYW5kIHRoZSBzdGFydHVwIGRhdGFzZXQgaXMgbm90IGV4dHJlbWVseSBsYXJnZSwgdGhlICoqRGVjaXNpb24gVHJlZSB3aXRoIEdpbmkgSW5kZXggYW5kIGEgNzA6MzAgc3BsaXQgdGhlIGlkZWFsIGNob2ljZSBmb3IgdGhlIHN0YXJ0dXAgZGF0YXNldC4qKg==